@ebowwa/seedinstallation 0.2.4 → 0.4.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 +1 -1
- package/README.md +36 -144
- package/dist/bootstrap.d.ts +5 -6
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +294 -215
- package/dist/clone.d.ts +1 -0
- package/dist/clone.d.ts.map +1 -0
- package/dist/clone.js +54 -68
- package/dist/device-auth.d.ts +4 -5
- package/dist/device-auth.d.ts.map +1 -0
- package/dist/device-auth.js +252 -164
- package/dist/index.d.ts +13 -12
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +941 -10
- package/dist/runtime.d.ts +9 -14
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +211 -114
- package/dist/sudo.d.ts +1 -0
- package/dist/sudo.d.ts.map +1 -0
- package/dist/sudo.js +123 -222
- package/dist/systemd.d.ts +207 -3
- package/dist/systemd.d.ts.map +1 -0
- package/dist/systemd.js +477 -177
- package/package.json +40 -40
package/dist/clone.js
CHANGED
|
@@ -1,74 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
else {
|
|
35
|
-
await run(["git", "checkout"], targetDir);
|
|
36
|
-
}
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
+
|
|
13
|
+
// clone.ts
|
|
14
|
+
async function clone(opts) {
|
|
15
|
+
const { repo, dest, ref, depth, sparse, cwd } = opts;
|
|
16
|
+
const repoName = dest ?? repoNameFrom(repo);
|
|
17
|
+
const targetDir = cwd ? `${cwd}/${repoName}` : repoName;
|
|
18
|
+
const args = ["git", "clone"];
|
|
19
|
+
if (depth)
|
|
20
|
+
args.push("--depth", String(depth));
|
|
21
|
+
if (ref && !sparse)
|
|
22
|
+
args.push("--branch", ref);
|
|
23
|
+
if (sparse)
|
|
24
|
+
args.push("--no-checkout", "--filter=blob:none");
|
|
25
|
+
args.push(repo, targetDir);
|
|
26
|
+
await run(args, cwd);
|
|
27
|
+
if (sparse) {
|
|
28
|
+
await run(["git", "sparse-checkout", "init", "--cone"], targetDir);
|
|
29
|
+
await run(["git", "sparse-checkout", "set", ...sparse], targetDir);
|
|
30
|
+
if (ref) {
|
|
31
|
+
await run(["git", "checkout", ref], targetDir);
|
|
32
|
+
} else {
|
|
33
|
+
await run(["git", "checkout"], targetDir);
|
|
37
34
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
}
|
|
36
|
+
const commit = (await run(["git", "rev-parse", "--short", "HEAD"], targetDir)).trim();
|
|
37
|
+
const checkedRef = (await run(["git", "rev-parse", "--abbrev-ref", "HEAD"], targetDir)).trim();
|
|
38
|
+
const absPath = (await run(["git", "rev-parse", "--show-toplevel"], targetDir)).trim();
|
|
39
|
+
return { path: absPath, ref: checkedRef, commit };
|
|
42
40
|
}
|
|
43
|
-
/** Extract repo name from a git URL */
|
|
44
41
|
function repoNameFrom(url) {
|
|
45
|
-
|
|
42
|
+
return url.replace(/\.git$/, "").split("/").pop();
|
|
46
43
|
}
|
|
47
|
-
/** Run a command and return stdout, throw on failure */
|
|
48
44
|
async function run(args, cwd) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
proc.on("close", (code) => {
|
|
63
|
-
if (code === 0) {
|
|
64
|
-
resolve(stdout);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
reject(new Error(`${args.join(" ")} failed (exit ${code}): ${stderr}`));
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
proc.on("error", (err) => {
|
|
71
|
-
reject(new Error(`${args.join(" ")} failed: ${err.message}`));
|
|
72
|
-
});
|
|
73
|
-
});
|
|
45
|
+
const proc = Bun.spawn(args, {
|
|
46
|
+
cwd,
|
|
47
|
+
stdout: "pipe",
|
|
48
|
+
stderr: "pipe"
|
|
49
|
+
});
|
|
50
|
+
const exitCode = await proc.exited;
|
|
51
|
+
const stdout = await new Response(proc.stdout).text();
|
|
52
|
+
if (exitCode !== 0) {
|
|
53
|
+
const stderr = await new Response(proc.stderr).text();
|
|
54
|
+
throw new Error(`${args.join(" ")} failed (exit ${exitCode}): ${stderr}`);
|
|
55
|
+
}
|
|
56
|
+
return stdout;
|
|
74
57
|
}
|
|
58
|
+
export {
|
|
59
|
+
clone
|
|
60
|
+
};
|
package/dist/device-auth.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles CLI tools that use device-code auth (Doppler, GitHub, Tailscale, etc.).
|
|
4
4
|
* Works with both local and SSH contexts via ExecContext from sudo.ts.
|
|
5
5
|
*/
|
|
6
|
-
import type { SudoOptions } from "./sudo
|
|
6
|
+
import type { SudoOptions } from "./sudo";
|
|
7
7
|
export interface DeviceAuthConfig {
|
|
8
8
|
/** CLI command name (e.g. "doppler", "gh", "tailscale") */
|
|
9
9
|
cli: string;
|
|
@@ -39,9 +39,7 @@ export interface DeviceAuthResult {
|
|
|
39
39
|
/** Error message if failed */
|
|
40
40
|
error?: string;
|
|
41
41
|
}
|
|
42
|
-
export interface DeviceAuthPollOptions {
|
|
43
|
-
/** Execution context for running commands */
|
|
44
|
-
context: SudoOptions["context"];
|
|
42
|
+
export interface DeviceAuthPollOptions extends SudoOptions {
|
|
45
43
|
/** Max poll attempts (default: 60) */
|
|
46
44
|
maxAttempts?: number;
|
|
47
45
|
/** Poll interval in ms (default: 2000) */
|
|
@@ -68,7 +66,7 @@ export declare function stripAnsi(text: string): string;
|
|
|
68
66
|
* { context: { type: "ssh", host: "1.2.3.4" } }
|
|
69
67
|
* );
|
|
70
68
|
*/
|
|
71
|
-
export declare function deviceAuth(config: DeviceAuthConfig, opts
|
|
69
|
+
export declare function deviceAuth(config: DeviceAuthConfig, opts?: DeviceAuthPollOptions): Promise<DeviceAuthResult>;
|
|
72
70
|
/**
|
|
73
71
|
* Check if a CLI is already authenticated.
|
|
74
72
|
*/
|
|
@@ -83,3 +81,4 @@ export declare const dopplerConfig: DeviceAuthConfig;
|
|
|
83
81
|
export declare const githubConfig: DeviceAuthConfig;
|
|
84
82
|
/** Tailscale CLI device-auth config */
|
|
85
83
|
export declare const tailscaleConfig: DeviceAuthConfig;
|
|
84
|
+
//# sourceMappingURL=device-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../device-auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAe,WAAW,EAAc,MAAM,QAAQ,CAAC;AAYnE,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,QAAQ,EAAE;QACR,uDAAuD;QACvD,GAAG,EAAE,MAAM,CAAC;QACZ,wDAAwD;QACxD,IAAI,EAAE,MAAM,CAAC;QACb,wDAAwD;QACxD,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAsB,SAAQ,WAAW;IACxD,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC9D;AAMD,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK9C;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAgF3B;AAeD;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC,EAChE,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAMD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,SAAS,CAAC,EACrD,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,IAAI,CAAC,CAaf;AAMD,qCAAqC;AACrC,eAAO,MAAM,aAAa,EAAE,gBAW3B,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,YAAY,EAAE,gBAW1B,CAAC;AAEF,uCAAuC;AACvC,eAAO,MAAM,eAAe,EAAE,gBAU7B,CAAC"}
|
package/dist/device-auth.js
CHANGED
|
@@ -1,176 +1,264 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
+
|
|
13
|
+
// sudo.ts
|
|
14
|
+
var exports_sudo = {};
|
|
15
|
+
__export(exports_sudo, {
|
|
16
|
+
writeFile: () => writeFile,
|
|
17
|
+
sudo: () => sudo,
|
|
18
|
+
serviceEnable: () => serviceEnable,
|
|
19
|
+
service: () => service,
|
|
20
|
+
pkgInstall: () => pkgInstall,
|
|
21
|
+
exec: () => exec
|
|
22
|
+
});
|
|
23
|
+
async function sudo(cmd, opts) {
|
|
24
|
+
const parts = Array.isArray(cmd) ? cmd : cmd.split(/\s+/);
|
|
25
|
+
const envPrefix = opts.env ? Object.entries(opts.env).map(([k, v]) => `${k}=${shellEscape(v)}`) : [];
|
|
26
|
+
const sudoCmd = ["sudo", ...envPrefix, ...parts];
|
|
27
|
+
return exec(sudoCmd, opts);
|
|
28
|
+
}
|
|
29
|
+
async function pkgInstall(packages, opts) {
|
|
30
|
+
const pm = opts.pm ?? "apt";
|
|
31
|
+
const envOverride = {
|
|
32
|
+
...opts.env,
|
|
33
|
+
...opts.nonInteractive !== false ? { DEBIAN_FRONTEND: "noninteractive" } : {}
|
|
34
|
+
};
|
|
35
|
+
await sudo(updateCmd[pm], { ...opts, env: envOverride, quiet: true });
|
|
36
|
+
return sudo([...installCmd[pm], ...packages], {
|
|
37
|
+
...opts,
|
|
38
|
+
env: envOverride
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function writeFile(path, content, opts) {
|
|
42
|
+
const op = opts.append ? "-a" : "";
|
|
43
|
+
const teeCmd = `tee ${op} ${shellEscape(path)}`.trim();
|
|
44
|
+
const result = await execPipe(content, ["sudo", teeCmd], opts);
|
|
45
|
+
if (result.ok && opts.mode) {
|
|
46
|
+
await sudo(["chmod", opts.mode, path], opts);
|
|
47
|
+
}
|
|
48
|
+
if (result.ok && opts.owner) {
|
|
49
|
+
await sudo(["chown", opts.owner, path], opts);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
async function service(name, action, opts) {
|
|
54
|
+
return sudo(["systemctl", action, name], opts);
|
|
55
|
+
}
|
|
56
|
+
async function serviceEnable(name, opts) {
|
|
57
|
+
return sudo(["systemctl", "enable", "--now", name], opts);
|
|
58
|
+
}
|
|
59
|
+
function buildSshPrefix(ctx) {
|
|
60
|
+
const parts = ["ssh", "-F", "/dev/null", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"];
|
|
61
|
+
if (ctx.keyPath)
|
|
62
|
+
parts.push("-i", ctx.keyPath);
|
|
63
|
+
else if (ctx.key)
|
|
64
|
+
parts.push("-i", ctx.key);
|
|
65
|
+
if (ctx.port)
|
|
66
|
+
parts.push("-p", String(ctx.port));
|
|
67
|
+
parts.push(`${ctx.user ?? "root"}@${ctx.host}`);
|
|
68
|
+
return parts;
|
|
69
|
+
}
|
|
7
70
|
async function exec(args, opts) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
71
|
+
const finalArgs = opts.context.type === "ssh" ? [...buildSshPrefix(opts.context), args.map(shellEscape).join(" ")] : args;
|
|
72
|
+
const proc = Bun.spawn(finalArgs, {
|
|
73
|
+
stdout: "pipe",
|
|
74
|
+
stderr: "pipe",
|
|
75
|
+
timeout: opts.timeout ?? 30000
|
|
76
|
+
});
|
|
77
|
+
const exitCode = await proc.exited;
|
|
78
|
+
const stdout = opts.quiet ? "" : await new Response(proc.stdout).text();
|
|
79
|
+
const stderr = await new Response(proc.stderr).text();
|
|
80
|
+
return { stdout, stderr, exitCode, ok: exitCode === 0 };
|
|
81
|
+
}
|
|
82
|
+
async function execPipe(input, args, opts) {
|
|
83
|
+
if (opts.context.type === "ssh") {
|
|
84
|
+
const sshPrefix = buildSshPrefix(opts.context);
|
|
85
|
+
const remoteCmd = args.join(" ");
|
|
86
|
+
const fullArgs = [...sshPrefix, remoteCmd];
|
|
87
|
+
const proc2 = Bun.spawn(fullArgs, {
|
|
88
|
+
stdin: new TextEncoder().encode(input),
|
|
89
|
+
stdout: "pipe",
|
|
90
|
+
stderr: "pipe",
|
|
91
|
+
timeout: opts.timeout ?? 30000
|
|
92
|
+
});
|
|
93
|
+
const exitCode2 = await proc2.exited;
|
|
94
|
+
const stdout2 = opts.quiet ? "" : await new Response(proc2.stdout).text();
|
|
95
|
+
const stderr2 = await new Response(proc2.stderr).text();
|
|
96
|
+
return { stdout: stdout2, stderr: stderr2, exitCode: exitCode2, ok: exitCode2 === 0 };
|
|
97
|
+
}
|
|
98
|
+
const proc = Bun.spawn(["sh", "-c", args.join(" ")], {
|
|
99
|
+
stdin: new TextEncoder().encode(input),
|
|
100
|
+
stdout: "pipe",
|
|
101
|
+
stderr: "pipe",
|
|
102
|
+
timeout: opts.timeout ?? 30000
|
|
103
|
+
});
|
|
104
|
+
const exitCode = await proc.exited;
|
|
105
|
+
const stdout = opts.quiet ? "" : await new Response(proc.stdout).text();
|
|
106
|
+
const stderr = await new Response(proc.stderr).text();
|
|
107
|
+
return { stdout, stderr, exitCode, ok: exitCode === 0 };
|
|
108
|
+
}
|
|
109
|
+
function shellEscape(s) {
|
|
110
|
+
if (/^[a-zA-Z0-9._\-\/=:@]+$/.test(s))
|
|
111
|
+
return s;
|
|
112
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
113
|
+
}
|
|
114
|
+
var installCmd, updateCmd;
|
|
115
|
+
var init_sudo = __esm(() => {
|
|
116
|
+
installCmd = {
|
|
117
|
+
apt: ["apt-get", "install", "-y"],
|
|
118
|
+
dnf: ["dnf", "install", "-y"],
|
|
119
|
+
apk: ["apk", "add", "--no-cache"]
|
|
120
|
+
};
|
|
121
|
+
updateCmd = {
|
|
122
|
+
apt: ["apt-get", "update", "-qq"],
|
|
123
|
+
dnf: ["dnf", "check-update"],
|
|
124
|
+
apk: ["apk", "update"]
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// device-auth.ts
|
|
129
|
+
async function exec2(args, opts) {
|
|
130
|
+
const { sudo: sudo2 } = await Promise.resolve().then(() => (init_sudo(), exports_sudo));
|
|
131
|
+
return sudo2(args, opts);
|
|
132
|
+
}
|
|
133
|
+
function stripAnsi(text) {
|
|
134
|
+
return text.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
135
|
+
}
|
|
136
|
+
async function deviceAuth(config, opts = {}) {
|
|
137
|
+
const {
|
|
138
|
+
cli,
|
|
139
|
+
loginCmd,
|
|
140
|
+
statusCmd,
|
|
141
|
+
patterns,
|
|
142
|
+
logFile,
|
|
143
|
+
pidFile,
|
|
144
|
+
statusFlags
|
|
145
|
+
} = config;
|
|
146
|
+
const maxAttempts = opts.maxAttempts ?? 60;
|
|
147
|
+
const intervalMs = opts.intervalMs ?? 2000;
|
|
148
|
+
const loginCmdStr = loginCmd ?? `${cli} login`;
|
|
149
|
+
const startResult = await exec2([`bash`, `-lc`, `nohup ${loginCmdStr} > ${logFile} 2>&1 & echo $!`], opts);
|
|
150
|
+
if (!startResult.ok) {
|
|
151
|
+
return { success: false, error: `Failed to start login: ${startResult.stderr}` };
|
|
152
|
+
}
|
|
153
|
+
const pid = startResult.stdout.trim();
|
|
154
|
+
if (pidFile) {
|
|
155
|
+
await exec2(["bash", `-lc`, `echo ${pid} > ${pidFile}`], opts);
|
|
156
|
+
}
|
|
157
|
+
await sleep(1000);
|
|
158
|
+
const extractResult = await exec2(["cat", logFile], { ...opts, quiet: false });
|
|
159
|
+
const logContent = stripAnsi(extractResult.stdout);
|
|
160
|
+
const urlMatch = patterns.url.exec(logContent);
|
|
161
|
+
const codeMatch = patterns.code.exec(logContent);
|
|
162
|
+
const url = urlMatch?.[1];
|
|
163
|
+
const code = codeMatch?.[1];
|
|
164
|
+
if (!url || !code) {
|
|
87
165
|
return {
|
|
88
|
-
|
|
166
|
+
success: false,
|
|
167
|
+
error: `Could not extract auth URL/code from log output. Log content:
|
|
168
|
+
${logContent}`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
172
|
+
const statusCmdStr = statusCmd ?? `${cli} status`;
|
|
173
|
+
const statusArgs = ["bash", "-lc", statusCmdStr];
|
|
174
|
+
if (statusFlags?.length)
|
|
175
|
+
statusArgs.push(...statusFlags);
|
|
176
|
+
const statusResult = await exec2(statusArgs, { ...opts, quiet: false });
|
|
177
|
+
const statusOutput = stripAnsi(statusResult.stdout);
|
|
178
|
+
if (patterns.success.test(statusOutput)) {
|
|
179
|
+
return {
|
|
180
|
+
success: true,
|
|
89
181
|
url,
|
|
90
182
|
code,
|
|
91
|
-
|
|
92
|
-
|
|
183
|
+
status: statusOutput
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
opts.onPoll?.(attempt, { success: false, url, code, status: statusOutput });
|
|
187
|
+
await sleep(intervalMs);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
url,
|
|
192
|
+
code,
|
|
193
|
+
error: `Login did not complete after ${maxAttempts} attempts. Last status: ${await getStatusOutput(config, opts)}`
|
|
194
|
+
};
|
|
93
195
|
}
|
|
94
196
|
async function getStatusOutput(config, opts) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
|
-
// Cleanup
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
/**
|
|
115
|
-
* Kill the background login process and clean up files.
|
|
116
|
-
*/
|
|
117
|
-
export async function cleanupDeviceAuth(config, opts) {
|
|
118
|
-
if (config.pidFile) {
|
|
119
|
-
// Read PID and kill process
|
|
120
|
-
const pidResult = await exec(["cat", config.pidFile], { ...opts, quiet: true });
|
|
121
|
-
if (pidResult.ok) {
|
|
122
|
-
const pid = pidResult.stdout.trim();
|
|
123
|
-
await exec(["kill", pid], { ...opts, quiet: true });
|
|
124
|
-
}
|
|
125
|
-
await exec(["rm", "-f", config.pidFile], { ...opts, quiet: true });
|
|
197
|
+
const statusCmdStr = config.statusCmd ?? `${config.cli} status`;
|
|
198
|
+
const result = await exec2(["bash", "-lc", statusCmdStr], { ...opts, quiet: false });
|
|
199
|
+
return stripAnsi(result.stdout);
|
|
200
|
+
}
|
|
201
|
+
async function isAuthed(config, opts) {
|
|
202
|
+
const statusCmdStr = config.statusCmd ?? `${config.cli} status`;
|
|
203
|
+
const result = await exec2(["bash", "-lc", statusCmdStr], { ...opts, quiet: false });
|
|
204
|
+
const output = stripAnsi(result.stdout);
|
|
205
|
+
return config.patterns.success.test(output);
|
|
206
|
+
}
|
|
207
|
+
async function cleanupDeviceAuth(config, opts) {
|
|
208
|
+
if (config.pidFile) {
|
|
209
|
+
const pidResult = await exec2(["cat", config.pidFile], { ...opts, quiet: true });
|
|
210
|
+
if (pidResult.ok) {
|
|
211
|
+
const pid = pidResult.stdout.trim();
|
|
212
|
+
await exec2(["kill", pid], { ...opts, quiet: true });
|
|
126
213
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
},
|
|
143
|
-
logFile: "/tmp/doppler-login.log",
|
|
144
|
-
pidFile: "/tmp/doppler-login.pid",
|
|
214
|
+
await exec2(["rm", "-f", config.pidFile], { ...opts, quiet: true });
|
|
215
|
+
}
|
|
216
|
+
await exec2(["rm", "-f", config.logFile], { ...opts, quiet: true });
|
|
217
|
+
}
|
|
218
|
+
var dopplerConfig = {
|
|
219
|
+
cli: "doppler",
|
|
220
|
+
loginCmd: "doppler login -y",
|
|
221
|
+
statusCmd: "doppler configure get token --scope /",
|
|
222
|
+
patterns: {
|
|
223
|
+
url: /https:\/\/(?:cli|dashboard)\.doppler\.com\/[a-z\-\/]+/i,
|
|
224
|
+
code: /(?:Your authentication code is:|Your auth code is:)\s*([A-Z0-9_]+(?:-[A-Z0-9_]+)*)/i,
|
|
225
|
+
success: /.{10,}/
|
|
226
|
+
},
|
|
227
|
+
logFile: "/tmp/doppler-login.log",
|
|
228
|
+
pidFile: "/tmp/doppler-login.pid"
|
|
145
229
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
pidFile: "/tmp/gh-login.pid",
|
|
230
|
+
var githubConfig = {
|
|
231
|
+
cli: "gh",
|
|
232
|
+
loginCmd: "GH_BROWSER=echo sh -c 'gh auth login -p https -h github.com < /dev/null'",
|
|
233
|
+
statusCmd: "gh auth status",
|
|
234
|
+
patterns: {
|
|
235
|
+
url: /(https:\/\/github\.com\/login\/device)/i,
|
|
236
|
+
code: /one-time code:\s*([A-Z0-9]{4}-[A-Z0-9]{4})/i,
|
|
237
|
+
success: /Logged in (?:as|to)/i
|
|
238
|
+
},
|
|
239
|
+
logFile: "/tmp/gh-login.log",
|
|
240
|
+
pidFile: "/tmp/gh-login.pid"
|
|
158
241
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
pidFile: "/tmp/tailscale-login.pid",
|
|
242
|
+
var tailscaleConfig = {
|
|
243
|
+
cli: "tailscale",
|
|
244
|
+
loginCmd: "tailscale up --authkey",
|
|
245
|
+
patterns: {
|
|
246
|
+
url: /https:\/\/login\.tailscale\.com\/[a-z0-9\/]+/,
|
|
247
|
+
code: /https:\/\/login\.tailscale\.com\/[a-z0-9\/]+/,
|
|
248
|
+
success: /Tailscale is running/i
|
|
249
|
+
},
|
|
250
|
+
logFile: "/tmp/tailscale-login.log",
|
|
251
|
+
pidFile: "/tmp/tailscale-login.pid"
|
|
170
252
|
};
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
// Utilities
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
253
|
function sleep(ms) {
|
|
175
|
-
|
|
254
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
176
255
|
}
|
|
256
|
+
export {
|
|
257
|
+
tailscaleConfig,
|
|
258
|
+
stripAnsi,
|
|
259
|
+
isAuthed,
|
|
260
|
+
githubConfig,
|
|
261
|
+
dopplerConfig,
|
|
262
|
+
deviceAuth,
|
|
263
|
+
cleanupDeviceAuth
|
|
264
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
export { clone } from "./clone
|
|
2
|
-
export type { CloneOptions, CloneResult } from "./clone
|
|
3
|
-
export { sudo, pkgInstall, writeFile, service, serviceEnable } from "./sudo
|
|
4
|
-
export type { ExecContext, SudoOptions, ExecResult, PkgOptions, PackageManager, WriteFileOptions, ServiceAction, } from "./sudo
|
|
5
|
-
export { installBun, addSystemPath, symlink, linkBinaries, getBunVersion, commandExists, } from "./runtime
|
|
6
|
-
export type { InstallBunOptions, PathOptions, SymlinkOptions, } from "./runtime
|
|
7
|
-
export { generateServiceUnit, createServiceUnit, enableService, disableService, startService, stopService, restartService, reloadService, getServiceStatus, enableAndStartService, serviceExists, getServiceLogs, } from "./systemd
|
|
8
|
-
export type { ServiceUnitOptions, ServiceResult, ServiceStatus, } from "./systemd
|
|
9
|
-
export { deviceAuth, isAuthed, cleanupDeviceAuth, stripAnsi, dopplerConfig, githubConfig, tailscaleConfig, } from "./device-auth
|
|
10
|
-
export type { DeviceAuthConfig, DeviceAuthResult, DeviceAuthPollOptions, } from "./device-auth
|
|
11
|
-
export { getBootstrapStatus, parseBootstrapStatus, initBootstrap, startPhase, completePhase, failPhase, completeBootstrap, failBootstrap, checkMarker, setMarker, removeMarker, waitForBootstrap, waitForMarker, } from "./bootstrap
|
|
12
|
-
export type { BootstrapPhase, BootstrapStatus, BootstrapPollOptions, } from "./bootstrap
|
|
1
|
+
export { clone } from "./clone";
|
|
2
|
+
export type { CloneOptions, CloneResult } from "./clone";
|
|
3
|
+
export { sudo, pkgInstall, writeFile, service, serviceEnable } from "./sudo";
|
|
4
|
+
export type { ExecContext, SudoOptions, ExecResult, PkgOptions, PackageManager, WriteFileOptions, ServiceAction, } from "./sudo";
|
|
5
|
+
export { installBun, addSystemPath, symlink, linkBinaries, getBunVersion, commandExists, } from "./runtime";
|
|
6
|
+
export type { InstallBunOptions, PathOptions, SymlinkOptions, } from "./runtime";
|
|
7
|
+
export { generateServiceUnit, createServiceUnit, enableService, disableService, startService, stopService, restartService, reloadService, getServiceStatus, enableAndStartService, serviceExists, getServiceLogs, hasSystemd, clearSystemdCache, safeSystemd, } from "./systemd";
|
|
8
|
+
export type { ServiceUnitOptions, ServiceResult, ServiceStatus, SystemdCheckResult, SafeSystemdOptions, } from "./systemd";
|
|
9
|
+
export { deviceAuth, isAuthed, cleanupDeviceAuth, stripAnsi, dopplerConfig, githubConfig, tailscaleConfig, } from "./device-auth";
|
|
10
|
+
export type { DeviceAuthConfig, DeviceAuthResult, DeviceAuthPollOptions, } from "./device-auth";
|
|
11
|
+
export { getBootstrapStatus, parseBootstrapStatus, initBootstrap, startPhase, completePhase, failPhase, completeBootstrap, failBootstrap, checkMarker, setMarker, removeMarker, waitForBootstrap, waitForMarker, } from "./bootstrap";
|
|
12
|
+
export type { BootstrapPhase, BootstrapStatus, BootstrapPollOptions, } from "./bootstrap";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC7E,YAAY,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,aAAa,GACd,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACL,UAAU,EACV,aAAa,EACb,OAAO,EACP,YAAY,EACZ,aAAa,EACb,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,cAAc,EAEd,UAAU,EACV,iBAAiB,EACjB,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,UAAU,EACV,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,aAAa,CAAC"}
|