aethel 0.2.6 → 0.3.1
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 +8 -0
- package/README.md +58 -8
- package/package.json +1 -1
- package/src/cli.js +92 -181
- package/src/core/auth.js +17 -5
- package/src/core/diff.js +7 -9
- package/src/core/drive-api.js +56 -13
- package/src/core/repository.js +309 -0
- package/src/core/snapshot.js +14 -3
- package/src/tui/app.js +530 -70
- package/src/tui/command-catalog.js +140 -0
- package/src/tui/commands.js +74 -0
- package/src/tui/index.js +12 -2
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export const COMMAND_CATALOG = [
|
|
2
|
+
{
|
|
3
|
+
name: "auth",
|
|
4
|
+
description: "Run OAuth initialization and verify Drive access",
|
|
5
|
+
template: "auth",
|
|
6
|
+
actions: [{ label: "Verify account", command: "auth" }],
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: "clean",
|
|
10
|
+
description: "List accessible Drive files and clean them",
|
|
11
|
+
template: "clean --shared-drives",
|
|
12
|
+
actions: [
|
|
13
|
+
{ label: "Preview My Drive", command: "clean" },
|
|
14
|
+
{ label: "Preview Shared Drives", command: "clean --shared-drives" },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "init",
|
|
19
|
+
description: "Initialise a sync workspace",
|
|
20
|
+
template: "init --local-path .",
|
|
21
|
+
actions: [{ label: "Init Current Directory", command: "init --local-path ." }],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "status",
|
|
25
|
+
description: "Show sync status",
|
|
26
|
+
template: "status",
|
|
27
|
+
actions: [{ label: "Show Status", command: "status" }],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "diff",
|
|
31
|
+
description: "Show detailed changes",
|
|
32
|
+
template: "diff --side all",
|
|
33
|
+
actions: [
|
|
34
|
+
{ label: "All Changes", command: "diff --side all" },
|
|
35
|
+
{ label: "Remote Only", command: "diff --side remote" },
|
|
36
|
+
{ label: "Local Only", command: "diff --side local" },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "add",
|
|
41
|
+
description: "Stage changes for commit",
|
|
42
|
+
template: "add --all",
|
|
43
|
+
actions: [{ label: "Stage All", command: "add --all" }],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "reset",
|
|
47
|
+
description: "Unstage changes",
|
|
48
|
+
template: "reset --all",
|
|
49
|
+
actions: [{ label: "Unstage All", command: "reset --all" }],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "commit",
|
|
53
|
+
description: "Apply staged changes and save snapshot",
|
|
54
|
+
template: "commit -m \"sync\"",
|
|
55
|
+
actions: [{ label: "Commit Staged", command: "commit -m \"sync\"" }],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "log",
|
|
59
|
+
description: "Show commit history",
|
|
60
|
+
template: "log -n 10",
|
|
61
|
+
actions: [{ label: "Show Recent History", command: "log -n 10" }],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "fetch",
|
|
65
|
+
description: "Check remote state",
|
|
66
|
+
template: "fetch",
|
|
67
|
+
actions: [{ label: "Fetch Remote State", command: "fetch" }],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "dedupe-folders",
|
|
71
|
+
description: "Detect duplicate remote folders",
|
|
72
|
+
template: "dedupe-folders",
|
|
73
|
+
actions: [{ label: "Dry Run", command: "dedupe-folders" }],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "pull",
|
|
77
|
+
description: "Download remote changes",
|
|
78
|
+
template: "pull",
|
|
79
|
+
actions: [
|
|
80
|
+
{ label: "Pull Now", command: "pull" },
|
|
81
|
+
{ label: "Dry Run", command: "pull --dry-run" },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "push",
|
|
86
|
+
description: "Upload local changes",
|
|
87
|
+
template: "push",
|
|
88
|
+
actions: [
|
|
89
|
+
{ label: "Push Now", command: "push" },
|
|
90
|
+
{ label: "Dry Run", command: "push --dry-run" },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "resolve",
|
|
95
|
+
description: "Resolve file conflicts",
|
|
96
|
+
template: "resolve",
|
|
97
|
+
actions: [
|
|
98
|
+
{ label: "List Conflicts", command: "resolve" },
|
|
99
|
+
{ label: "Use Ours", command: "resolve --ours" },
|
|
100
|
+
{ label: "Use Theirs", command: "resolve --theirs" },
|
|
101
|
+
{ label: "Keep Both", command: "resolve --both" },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "ignore",
|
|
106
|
+
description: "Manage .aethelignore patterns",
|
|
107
|
+
template: "ignore list",
|
|
108
|
+
actions: [
|
|
109
|
+
{ label: "List Rules", command: "ignore list" },
|
|
110
|
+
{ label: "Create Default File", command: "ignore create" },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "show",
|
|
115
|
+
description: "Show a commit or snapshot",
|
|
116
|
+
template: "show HEAD",
|
|
117
|
+
actions: [
|
|
118
|
+
{ label: "Show HEAD", command: "show HEAD" },
|
|
119
|
+
{ label: "Show HEAD Verbose", command: "show --verbose HEAD" },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "restore",
|
|
124
|
+
description: "Restore files from the last snapshot",
|
|
125
|
+
template: "restore path/to/file",
|
|
126
|
+
actions: [],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "rm",
|
|
130
|
+
description: "Delete local files and stage remote deletion",
|
|
131
|
+
template: "rm path/to/file",
|
|
132
|
+
actions: [],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "mv",
|
|
136
|
+
description: "Move or rename a local file",
|
|
137
|
+
template: "mv old/path new/path",
|
|
138
|
+
actions: [],
|
|
139
|
+
},
|
|
140
|
+
];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
function parseShellWords(input) {
|
|
2
|
+
const tokens = [];
|
|
3
|
+
let current = "";
|
|
4
|
+
let quote = null;
|
|
5
|
+
let escaping = false;
|
|
6
|
+
|
|
7
|
+
for (const char of input.trim()) {
|
|
8
|
+
if (escaping) {
|
|
9
|
+
current += char;
|
|
10
|
+
escaping = false;
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (char === "\\") {
|
|
15
|
+
escaping = true;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (quote) {
|
|
20
|
+
if (char === quote) {
|
|
21
|
+
quote = null;
|
|
22
|
+
} else {
|
|
23
|
+
current += char;
|
|
24
|
+
}
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (char === "'" || char === "\"") {
|
|
29
|
+
quote = char;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (/\s/.test(char)) {
|
|
34
|
+
if (current) {
|
|
35
|
+
tokens.push(current);
|
|
36
|
+
current = "";
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
current += char;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (escaping) {
|
|
45
|
+
current += "\\";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (quote) {
|
|
49
|
+
throw new Error("Unterminated quote in command.");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (current) {
|
|
53
|
+
tokens.push(current);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return tokens;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function parseCommandInput(input) {
|
|
60
|
+
const tokens = parseShellWords(input);
|
|
61
|
+
if (tokens[0] === "aethel") {
|
|
62
|
+
tokens.shift();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (tokens.length === 0) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (tokens[0] === "tui") {
|
|
70
|
+
throw new Error("Cannot launch `tui` from inside the TUI.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return tokens;
|
|
74
|
+
}
|
package/src/tui/index.js
CHANGED
|
@@ -2,9 +2,19 @@ import React from "react";
|
|
|
2
2
|
import { render } from "ink";
|
|
3
3
|
import { AethelTui } from "./app.js";
|
|
4
4
|
|
|
5
|
-
export async function runTui({
|
|
5
|
+
export async function runTui({
|
|
6
|
+
repo,
|
|
7
|
+
includeSharedDrives = false,
|
|
8
|
+
cliPath = null,
|
|
9
|
+
cliArgs = [],
|
|
10
|
+
} = {}) {
|
|
6
11
|
const instance = render(
|
|
7
|
-
React.createElement(AethelTui, {
|
|
12
|
+
React.createElement(AethelTui, {
|
|
13
|
+
repo,
|
|
14
|
+
includeSharedDrives,
|
|
15
|
+
cliPath,
|
|
16
|
+
cliArgs,
|
|
17
|
+
})
|
|
8
18
|
);
|
|
9
19
|
await instance.waitUntilExit();
|
|
10
20
|
}
|