@inoku/lepan 0.7.72 → 0.7.74
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 +6 -6
- package/USER_MANUAL.md +11 -8
- package/bin/lifedrive.js +85 -17
- package/dist/lifedrive-bundle.tar.gz +2 -2
- package/dist/lifedrive-bundle.tar.gz.sha256 +1 -1
- package/dist/lifedrive-install.sh +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,9 +97,9 @@ The Linux/macOS legacy browser-owner setup uses the built-in AV1 registration en
|
|
|
97
97
|
|
|
98
98
|
LifeDrive requires Leither V0.24.11 or newer. Linux/macOS browser-owner setup creates a separate Ed25519 `sodiumv2` identity for the primary browser. Windows prints a private ten-minute invitation for the first paired phone; later users and devices are managed from a paired device.
|
|
99
99
|
|
|
100
|
-
After successful npm setup, the installer displays the QR code at the end of its output whenever the
|
|
100
|
+
After successful npm setup, the installer displays the QR code at the end of its output whenever the terminal is wide enough, regardless of its height. You can scroll vertically. If the terminal is too narrow, it automatically opens the full-size QR image in the computer's image viewer. In the iPhone or Android app, open **Settings → Set up users → Scan QR code** and scan the displayed code. A private `lepan-pairing-<id>.png` is also saved, with its path printed as a fallback for remote sessions or computers without an image viewer. A private `.json` invitation is saved alongside it for apps without a setup scanner: transfer that file to the phone and use **Choose identity file**. These files are saved in the Leither root on Linux/macOS and its private `.lifedrive-household` directory on Windows. Household invitations expire ten minutes after creation; legacy identity codes contain a device private key, so keep them private and delete the transfer copies after pairing. Legacy `--add-device` and `--upgrade` also produce a code when an identity is available; household upgrades preserve existing pairings without issuing new invitations.
|
|
101
101
|
|
|
102
|
-
If the terminal is too
|
|
102
|
+
If the terminal is too narrow, the output gives the required number of columns,
|
|
103
103
|
a width marker, and a command to redisplay the code after resizing. Run it on the node
|
|
104
104
|
when connected over SSH. For npm installations, the command has this form:
|
|
105
105
|
|
|
@@ -107,7 +107,7 @@ when connected over SSH. For npm installations, the command has this form:
|
|
|
107
107
|
npx --yes @inoku/lepan@latest --show-qr '/path/to/lepan-pairing.json'
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Redisplaying shows only the QR and a privacy reminder, or a short
|
|
110
|
+
Redisplaying shows only the QR and a privacy reminder, or a short width warning
|
|
111
111
|
and width marker if it cannot fit. It does not repeat the setup instructions or
|
|
112
112
|
create another image file. Terminal QR codes use a smaller payload and lower
|
|
113
113
|
error correction; the saved PNG keeps medium error correction. The full original
|
|
@@ -160,9 +160,9 @@ private and expires in ten minutes; rerun the same command if it expires before
|
|
|
160
160
|
being claimed. The account service briefly restarts, while Leither keeps running.
|
|
161
161
|
Normal phone-to-phone invitations still require device-manager approval.
|
|
162
162
|
|
|
163
|
-
First-account pairing prepares the file catalog before opening Files
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
First-account pairing prepares the file catalog before opening Files. The phone app
|
|
164
|
+
then adds `LePan Guide.md`, and a copy in the phone's language when LePan is translated
|
|
165
|
+
into it. A guide already in Files is not added again. First setup also grants each account
|
|
166
166
|
write access only to its own node staging directory when Leither requires it.
|
|
167
167
|
|
|
168
168
|
An existing installation is backed up before replacement. The explicit `--upgrade` path requires the saved application and key-auth owner state before changing files, synchronizes the same published application MID, and does not rerun device or domain setup. This release intentionally does not migrate the earlier password-owner prototype. Application files are active only after Leither advances the application from `cur` to `last`; a publication timeout leaves the prior `last` version serving users.
|
package/USER_MANUAL.md
CHANGED
|
@@ -147,7 +147,7 @@ This keeps what step 2.1 installed and adds users and phones:
|
|
|
147
147
|
The setup invitation is what makes a phone the first user of this node. Treat it
|
|
148
148
|
like a key: do not post it, email it to others, or paste it anywhere public.
|
|
149
149
|
|
|
150
|
-
If the terminal is too
|
|
150
|
+
If the terminal is too narrow for the QR, widen it to the number of columns
|
|
151
151
|
printed by setup, then run the printed `--show-qr` command. Over SSH, run the
|
|
152
152
|
command on the node in that same terminal. It displays the saved code without
|
|
153
153
|
installing or upgrading again. You can also open the saved PNG image or transfer
|
|
@@ -158,8 +158,9 @@ For npm installations:
|
|
|
158
158
|
npx --yes @inoku/lepan@latest --show-qr '/path/to/lepan-pairing.json'
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
This command shows only the QR and a privacy reminder.
|
|
162
|
-
|
|
161
|
+
This command shows only the QR and a privacy reminder. There is no minimum
|
|
162
|
+
height; scroll vertically as needed. If the terminal is too narrow, it prints
|
|
163
|
+
the required width and a width marker so you can widen it and retry. It does not
|
|
163
164
|
repeat the setup instructions or create another image file. Terminal QR codes use
|
|
164
165
|
a smaller encoding; the saved PNG retains stronger error correction.
|
|
165
166
|
|
|
@@ -181,10 +182,12 @@ Within ten minutes of the invitation being printed:
|
|
|
181
182
|
4. Tap **Scan QR code** and point the phone at the terminal or saved image. You can also use **Paste node identity** or **Choose identity file**.
|
|
182
183
|
|
|
183
184
|
The phone creates its own device key, claims the node, and opens your personal
|
|
184
|
-
drive after its file list is ready.
|
|
185
|
-
a short guide to
|
|
186
|
-
|
|
187
|
-
it
|
|
185
|
+
drive after its file list is ready. When a phone first pairs, the app adds **LePan Guide.md**
|
|
186
|
+
to Files, a short guide to the tabs, photo backup, sharing, adding devices and keeping
|
|
187
|
+
the computer on. If LePan is translated into the phone's language (简体中文, 繁體中文 or
|
|
188
|
+
日本語) it adds a copy in that language too. A guide already in Files is not added
|
|
189
|
+
again, so another phone does not duplicate it; one you delete comes back the next
|
|
190
|
+
time a phone pairs. Tap a Markdown file in Files to read it formatted. You are now the node's first user and its **administrator**, and
|
|
188
191
|
this phone is a **device manager**.
|
|
189
192
|
|
|
190
193
|
You can close the setup screen without pairing to look around the app. Tapping an
|
|
@@ -229,7 +232,7 @@ To install a specific release instead of the latest, name it, for example
|
|
|
229
232
|
so a version number always means the same files.
|
|
230
233
|
|
|
231
234
|
Upgrade output shows the existing identity path or saved-pairing directory and a
|
|
232
|
-
help command. When a QR cannot fit, it shows the required terminal
|
|
235
|
+
help command. When a QR cannot fit, it shows the required terminal width and the
|
|
233
236
|
`--show-qr` command to run after resizing the window. Use `--help` for file import,
|
|
234
237
|
identity expiry, and device-management instructions. Household
|
|
235
238
|
upgrades preserve paired phones and do not issue or renew an invitation; to
|
package/bin/lifedrive.js
CHANGED
|
@@ -17,12 +17,73 @@ const pairingCommand = path.basename(path.resolve(__dirname, "..")) === "package
|
|
|
17
17
|
? `${process.platform === "win32" ? "& " : ""}${quoteArgument(process.execPath)} ${quoteArgument(__filename)}`
|
|
18
18
|
: `npx --yes @inoku/lepan@${/^\d+\.\d+\.\d+$/.test(packageVersion) ? packageVersion : "latest"}`;
|
|
19
19
|
|
|
20
|
+
// Opt-in: print the smaller device QR that only newer LePan apps can read. Older
|
|
21
|
+
// apps accept the full identity QR, so that stays the default. The flag belongs
|
|
22
|
+
// to this wrapper; the shell and PowerShell installers do not know it.
|
|
23
|
+
const compactQr = process.argv.includes("--compact-qr");
|
|
24
|
+
if (compactQr) process.argv = process.argv.filter(argument => argument !== "--compact-qr");
|
|
25
|
+
const qrFlag = compactQr ? " --compact-qr" : "";
|
|
26
|
+
|
|
20
27
|
function quoteArgument(value) {
|
|
21
28
|
return process.platform === "win32"
|
|
22
29
|
? `'${value.replace(/'/g, "''")}'`
|
|
23
30
|
: `'${value.replace(/'/g, "'\\''")}'`;
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
// A device identity's signer_info is a Go-gob certificate of about 770 characters
|
|
34
|
+
// that repeats the device ID and public key, plus constant type metadata. Only the
|
|
35
|
+
// signature and the certificate name are unique, so the compact QR carries those and
|
|
36
|
+
// the app rebuilds the same bytes. buildSignerInfo must stay identical to
|
|
37
|
+
// CompactIdentity in the Android and iOS apps.
|
|
38
|
+
const CERT_PREFIX = Buffer.from("307f030101085369676e496e666f01ff80000102010c426173655369676e496e666f01ff8200010444617461010c0000002eff810301010c426173655369676e496e666f01ff82000101010e426173655369676e496e666f563101ff8400000065ff830301010e426173655369676e496e666f563101ff8400010601095369676e6174757265010a0001085369676e65724944010c00010a5369676e6572496e666f010a0001074b657954797065010c00010756657273696f6e0106000102504b010a000000", "hex");
|
|
39
|
+
const CERT_KEY_PREFIX = Buffer.from("0b0c0008736f6469756d76322f7f0301010f536f6469756d4b657950616972563201ff80000101010d536f6469756d4b65795061697201ff8200000029ff810301010d536f6469756d4b65795061697201ff820001020102536b010a000102506b010a00000027ff80010220", "hex");
|
|
40
|
+
const CERT_BODY_HEAD = Buffer.from("ff8001010140", "hex");
|
|
41
|
+
const CERT_AFTER_ID = Buffer.concat([Buffer.from("0208", "hex"), Buffer.from("sodiumv2"), Buffer.from("0101000001", "hex")]);
|
|
42
|
+
// Go gob unsigned integer: one byte below 128, else a byte-count marker and the
|
|
43
|
+
// big-endian value.
|
|
44
|
+
function gobUint(n) {
|
|
45
|
+
if (n < 128) return Buffer.from([n]);
|
|
46
|
+
const bytes = [];
|
|
47
|
+
for (let v = n; v > 0; v = Math.floor(v / 256)) bytes.unshift(v % 256);
|
|
48
|
+
return Buffer.from([256 - bytes.length, ...bytes]);
|
|
49
|
+
}
|
|
50
|
+
function buildSignerInfo(signature, uid, publicKey, name) {
|
|
51
|
+
const keyBlob = Buffer.concat([CERT_KEY_PREFIX, publicKey, Buffer.from([0, 0])]);
|
|
52
|
+
const certificate = Buffer.from(`CertPK=${keyBlob.toString("base64url")}==;CertPKID=${uid};name=${name};`, "utf8");
|
|
53
|
+
const body = Buffer.concat([CERT_BODY_HEAD, signature, Buffer.from([1, 27]), Buffer.from(uid, "utf8"),
|
|
54
|
+
CERT_AFTER_ID, gobUint(certificate.length), certificate, Buffer.from([0])]);
|
|
55
|
+
return Buffer.concat([CERT_PREFIX, gobUint(body.length), body]).toString("base64");
|
|
56
|
+
}
|
|
57
|
+
function splitSignerInfo(signerInfo, uid, publicKey) {
|
|
58
|
+
const raw = Buffer.from(signerInfo, "base64");
|
|
59
|
+
const start = CERT_PREFIX.length + 3;
|
|
60
|
+
if (raw.length < start + 6 + 64 + 2 + 27) return null;
|
|
61
|
+
const signature = raw.subarray(start + 6, start + 70);
|
|
62
|
+
const text = raw.toString("utf8");
|
|
63
|
+
const found = /;name=([^;]*);\x00$/.exec(text);
|
|
64
|
+
if (!found) return null;
|
|
65
|
+
const rebuilt = buildSignerInfo(signature, uid, publicKey, found[1]);
|
|
66
|
+
return rebuilt === signerInfo ? { signature, name: found[1] } : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function compactDeviceQrPayload(pairing) {
|
|
70
|
+
const seed = Buffer.from(pairing.private_key, "base64url");
|
|
71
|
+
const publicKey = Buffer.from(pairing.public_key, "base64url");
|
|
72
|
+
if (seed.length === 64 && publicKey.length === 32 && seed.subarray(32).equals(publicKey)) {
|
|
73
|
+
const split = splitSignerInfo(pairing.signer_info, pairing.uid, publicKey);
|
|
74
|
+
if (split) {
|
|
75
|
+
const compact = {
|
|
76
|
+
t: "ldq", a: pairing.app_id, n: pairing.node_id, u: pairing.uid, s: seed.subarray(0, 32).toString("base64url"),
|
|
77
|
+
g: split.signature.toString("base64url"), l: pairing.label, o: pairing.public_username, c: split.name,
|
|
78
|
+
};
|
|
79
|
+
seed.fill(0);
|
|
80
|
+
return JSON.stringify(compact);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
seed.fill(0);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
26
87
|
function pairingQrPayload(pairing) {
|
|
27
88
|
const legacy = pairing.type === "lifedrive-device-identity";
|
|
28
89
|
// Preserve every field consumed by the existing mobile importers. The full
|
|
@@ -34,6 +95,11 @@ function pairingQrPayload(pairing) {
|
|
|
34
95
|
"type", "version", "app_id", "node_id", "endpoint", "transport",
|
|
35
96
|
"transport_key", "certificate_sha256", "request_id", "secret",
|
|
36
97
|
];
|
|
98
|
+
// A certificate this wrapper cannot rebuild exactly keeps the full identity QR.
|
|
99
|
+
if (legacy && compactQr) {
|
|
100
|
+
const compact = compactDeviceQrPayload(pairing);
|
|
101
|
+
if (compact) return compact;
|
|
102
|
+
}
|
|
37
103
|
const qr = Object.fromEntries(fields.filter(key => Object.hasOwn(pairing, key)).map(key => [key, pairing[key]]));
|
|
38
104
|
if (legacy && typeof qr.private_key === "string" && typeof qr.public_key === "string") {
|
|
39
105
|
// sodium's 64-byte secret includes a duplicate public key. Both Android and
|
|
@@ -113,7 +179,7 @@ async function showPairingCode(resultPath, savedFile = false) {
|
|
|
113
179
|
: "This is a persistent device identity; it does not expire after ten minutes. It remains usable while the device is authorized.\nIt contains your device private key. Keep the original safe and offline; delete transfer copies after pairing.";
|
|
114
180
|
const expiry = pairing.type !== "lifedrive-device-identity" && Number.isFinite(pairing.expires)
|
|
115
181
|
? `\nInvitation expiry: ${new Date(pairing.expires * 1000).toISOString()} (UTC).\n${pairing.expires <= Date.now() / 1000 ? "This invitation has expired. Create a fresh invitation before pairing.\n" : ""}` : "";
|
|
116
|
-
const reopenHint = `\nTo show this QR again, run on this computer (on the node for an SSH session):\n ${pairingCommand} --show-qr ${quoteArgument(identityPath)}\nThis only displays the saved pairing code; it does not install or upgrade LePan.\n`;
|
|
182
|
+
const reopenHint = `\nTo show this QR again, run on this computer (on the node for an SSH session):\n ${pairingCommand} --show-qr ${quoteArgument(identityPath)}${qrFlag}\nThis only displays the saved pairing code; it does not install or upgrade LePan.\n`;
|
|
117
183
|
const nodeRoot = process.platform === "win32" && path.basename(directory) === ".lifedrive-household"
|
|
118
184
|
? path.dirname(directory) : directory;
|
|
119
185
|
// A saved file may have been moved off the node. Its new parent is not proof
|
|
@@ -161,19 +227,17 @@ async function showPairingCode(resultPath, savedFile = false) {
|
|
|
161
227
|
}
|
|
162
228
|
return;
|
|
163
229
|
}
|
|
164
|
-
//
|
|
165
|
-
//
|
|
230
|
+
// Prevent horizontal wrapping; terminal height does not restrict printing.
|
|
231
|
+
// Users can scroll vertically when the QR is taller than the window.
|
|
166
232
|
const width = modules.size + options.margin * 2;
|
|
167
233
|
const widthMarker = `|${"-".repeat(width - 1)}|`;
|
|
168
|
-
const height = Math.ceil(width / 2);
|
|
169
234
|
const fitsWidth = process.stdout.isTTY && process.stdout.columns > width;
|
|
170
|
-
const fitsScreen = fitsWidth && process.stdout.rows >= height + 3;
|
|
171
235
|
// Redisplaying is deliberately quiet and creates no additional files.
|
|
172
236
|
if (savedFile) {
|
|
173
237
|
if (!process.stdout.isTTY) {
|
|
174
238
|
process.stdout.write("Run --show-qr in an interactive terminal.\n");
|
|
175
|
-
} else if (!
|
|
176
|
-
process.stdout.write(`Terminal too
|
|
239
|
+
} else if (!fitsWidth) {
|
|
240
|
+
process.stdout.write(`Terminal too narrow for the QR code: ${width + 1} columns needed.\n${widthMarker}\nWiden the terminal, then run the same command again.\n`);
|
|
177
241
|
} else {
|
|
178
242
|
process.stdout.write("Keep it private.\n");
|
|
179
243
|
process.stdout.write(expiry);
|
|
@@ -181,15 +245,15 @@ async function showPairingCode(resultPath, savedFile = false) {
|
|
|
181
245
|
}
|
|
182
246
|
return;
|
|
183
247
|
}
|
|
184
|
-
const imageOpened = !
|
|
248
|
+
const imageOpened = !fitsWidth && openPairingImage(imagePath);
|
|
185
249
|
if (compact) {
|
|
186
250
|
process.stdout.write("\nHow to pair your phone:\nOpen LePan > Settings > Set up users > Scan QR code.\n");
|
|
187
251
|
if (imageOpened) process.stdout.write("\nThe QR image has opened automatically.\n");
|
|
188
|
-
if (!
|
|
252
|
+
if (!fitsWidth) {
|
|
189
253
|
process.stdout.write(process.stdout.isTTY
|
|
190
|
-
? `\nThe QR code needs ${width + 1} columns
|
|
254
|
+
? `\nThe QR code needs ${width + 1} columns. Widen this terminal or open the saved image.\n${widthMarker}\nTo display it here, widen and run:\n`
|
|
191
255
|
: "\nTo display the QR code, run in an interactive terminal:\n");
|
|
192
|
-
process.stdout.write(`\n ${pairingCommand} --show-qr ${quoteArgument(identityPath)}\n`);
|
|
256
|
+
process.stdout.write(`\n ${pairingCommand} --show-qr ${quoteArgument(identityPath)}${qrFlag}\n`);
|
|
193
257
|
}
|
|
194
258
|
process.stdout.write(`\nSaved QR image: ${imagePath}\nPairing file (Choose identity file): ${identityPath}\n`);
|
|
195
259
|
process.stdout.write(expiry);
|
|
@@ -199,11 +263,11 @@ async function showPairingCode(resultPath, savedFile = false) {
|
|
|
199
263
|
process.stdout.write("Open LePan > Settings > Set up users > Scan QR code.\n");
|
|
200
264
|
process.stdout.write(imageOpened
|
|
201
265
|
? "The QR image has opened automatically. Scan it with your phone.\n"
|
|
202
|
-
:
|
|
266
|
+
: fitsWidth ? "Scan the QR code below with your phone.\n"
|
|
203
267
|
: process.stdout.isTTY
|
|
204
|
-
? `The QR code needs ${width + 1} columns
|
|
268
|
+
? `The QR code needs ${width + 1} columns. Widen this terminal or open the saved image.\n`
|
|
205
269
|
: "A QR code cannot be displayed in this output. Run the --show-qr command below in an interactive terminal, or open the saved QR image.\n");
|
|
206
|
-
if (!
|
|
270
|
+
if (!fitsWidth && process.stdout.isTTY) process.stdout.write(`${widthMarker}\n`);
|
|
207
271
|
process.stdout.write(`${privacyNote}\n`);
|
|
208
272
|
process.stdout.write(expiry);
|
|
209
273
|
process.stdout.write(`\nSaved QR image: ${imagePath}\n`);
|
|
@@ -216,7 +280,7 @@ async function showPairingCode(resultPath, savedFile = false) {
|
|
|
216
280
|
process.stdout.write("Redisplaying the QR does not extend the invitation's ten-minute expiry.\n");
|
|
217
281
|
}
|
|
218
282
|
}
|
|
219
|
-
if (
|
|
283
|
+
if (fitsWidth) {
|
|
220
284
|
printTerminalQr(modules, options.margin);
|
|
221
285
|
}
|
|
222
286
|
}
|
|
@@ -297,12 +361,16 @@ async function main() {
|
|
|
297
361
|
if (argumentsList.includes("--help") || argumentsList.includes("-h")) {
|
|
298
362
|
process.stdout.write(`Pairing help:
|
|
299
363
|
--show-qr FILE Display a saved identity or invitation as a QR code.
|
|
364
|
+
--compact-qr Use the smaller device QR (a much smaller code to scan).
|
|
365
|
+
Needs a LePan app version that supports it; add it to an
|
|
366
|
+
install, upgrade or --show-qr command.
|
|
300
367
|
|
|
301
368
|
Example:
|
|
302
369
|
${pairingCommand} --show-qr '/path/to/lepan-pairing.json'
|
|
303
370
|
|
|
304
|
-
Run pairing commands on the computer running Leither. If the
|
|
305
|
-
|
|
371
|
+
Run pairing commands on the computer running Leither. If the terminal is too narrow,
|
|
372
|
+
widen it to the printed number of columns and run --show-qr again.
|
|
373
|
+
There is no minimum height; scroll vertically as needed.
|
|
306
374
|
You can also open the saved PNG, or transfer the JSON privately to your phone
|
|
307
375
|
and use LePan > Settings > Set up users > Choose identity file.
|
|
308
376
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/dist/lifedrive-bundle.tar.gz
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 44310147 bytes
|
|
4
|
+
sha256: 5a4a68d55e18d0fc45f2f6f04cb6b7216f68938a93cf9c405be5b6a5420f4e5f
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5a4a68d55e18d0fc45f2f6f04cb6b7216f68938a93cf9c405be5b6a5420f4e5f lifedrive-bundle.tar.gz
|
|
@@ -535,7 +535,7 @@ wait_for_leither() {
|
|
|
535
535
|
local version="$1" port="$2" previous_pid="${3:-}" attempt response current_pid
|
|
536
536
|
for attempt in {1..30}; do
|
|
537
537
|
current_pid=$(leither_service_pid || true)
|
|
538
|
-
response=$(curl --noproxy '*' -fsS --max-time 2 "http://127.0.0.1:$port/getvar?name=ver" 2>/dev/null || true)
|
|
538
|
+
response=$(curl -q --noproxy '*' -fsS --max-time 2 "http://127.0.0.1:$port/getvar?name=ver" 2>/dev/null || true)
|
|
539
539
|
if [[ -z "$previous_pid" || "$current_pid" != "$previous_pid" ]] &&
|
|
540
540
|
leither_service_owns_process &&
|
|
541
541
|
node -e 'try { process.exit(JSON.parse(process.argv[1]).replace(/^V/, "") === process.argv[2] ? 0 : 1); } catch { process.exit(1); }' "$response" "$version"; then
|
|
@@ -799,7 +799,10 @@ if [[ -s "$LEITHER_WORKDIR/lifeDrive.owner" && "$(sed -n '1p' "$LEITHER_WORKDIR/
|
|
|
799
799
|
echo "This key-auth release intentionally requires a fresh LePan owner state." >&2
|
|
800
800
|
exit 1
|
|
801
801
|
fi
|
|
802
|
-
|
|
802
|
+
# --recover-device reconnects an account of a household that already exists; it creates
|
|
803
|
+
# no owner, so key-auth state left behind from before the node became a household must
|
|
804
|
+
# not stop it (minipc kept its lifeDrive.owner and answered "an initialized LePan exists").
|
|
805
|
+
if [[ -s "$LEITHER_WORKDIR/lifeDrive.owner" && -z "$HOUSEHOLD_CONFIG" && -z "$RECOVER_DEVICE" ]] && (( ! UPGRADE_ONLY && ! MANAGEMENT_ONLY )); then
|
|
803
806
|
echo "LePan installation stopped: an initialized LePan already exists at $LEITHER_WORKDIR." >&2
|
|
804
807
|
echo "Use --upgrade for application files or --add-device for another browser." >&2
|
|
805
808
|
exit 1
|