@archildata/just-bash 0.1.13 → 0.8.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/README.md +6 -3
- package/dist/index.cjs +571 -458
- package/dist/index.d.cts +60 -17
- package/dist/index.d.ts +60 -17
- package/dist/index.js +561 -469
- package/dist/shell.js +170 -108
- package/package.json +2 -2
package/dist/shell.js
CHANGED
|
@@ -3,45 +3,183 @@
|
|
|
3
3
|
// bin/shell.ts
|
|
4
4
|
import * as readline from "readline";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
import { ArchilClient } from "@archildata/client";
|
|
6
|
+
import { ArchilClient, Archil } from "@archildata/client";
|
|
7
7
|
import { Bash } from "just-bash";
|
|
8
|
-
import { ArchilFs } from "@archildata/just-bash";
|
|
8
|
+
import { ArchilFs, createArchilCommand } from "@archildata/just-bash";
|
|
9
9
|
var program = new Command();
|
|
10
|
-
program.name("archil-shell").description("Interactive bash shell with Archil filesystem").version("0.1.0").argument("[
|
|
10
|
+
program.name("archil-shell").description("Interactive bash shell with Archil filesystem").version("0.1.0").argument("[target]", "S3 bucket (s3://bucket) or region (e.g., aws-us-east-1)").argument("[disk]", "Disk name (e.g., myaccount/mydisk) - only when target is region").option("-r, --region <region>", "Region identifier (e.g., aws-us-east-1)").option("-d, --disk <disk>", "Disk name (e.g., myaccount/mydisk)").option("-k, --api-key <key>", "API key for disk management (required for S3 bucket mode)").option("-t, --token <token>", "Auth token for direct connection (defaults to IAM)").option("-l, --log-level <level>", "Log level: trace, debug, info, warn, error").option("--bucket-region <region>", "AWS region for the S3 bucket (default: us-east-1)").option("--bucket-prefix <prefix>", "Path prefix within the bucket").addHelpText("after", `
|
|
11
|
+
Quick Start (S3 bucket mode):
|
|
12
|
+
Connect directly to an S3 bucket - creates a disk if needed:
|
|
13
|
+
$ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx
|
|
14
|
+
$ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx --bucket-region us-west-2
|
|
15
|
+
|
|
16
|
+
AWS credentials are read from environment:
|
|
17
|
+
$ AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx
|
|
18
|
+
|
|
19
|
+
Traditional usage (direct connection):
|
|
20
|
+
$ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
21
|
+
$ npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk
|
|
22
|
+
$ ARCHIL_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
23
|
+
|
|
24
|
+
Subdirectory mounting (mount a subdirectory as the root):
|
|
25
|
+
$ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk:/data/project
|
|
26
|
+
|
|
11
27
|
Environment variables:
|
|
28
|
+
ARCHIL_API_KEY API key for disk management
|
|
12
29
|
ARCHIL_REGION Fallback for --region
|
|
13
30
|
ARCHIL_DISK Fallback for --disk
|
|
14
31
|
ARCHIL_TOKEN Fallback for --token (recommended for secrets)
|
|
15
32
|
ARCHIL_LOG_LEVEL Fallback for --log-level
|
|
16
|
-
|
|
17
|
-
Examples:
|
|
18
|
-
$ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
19
|
-
$ npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk
|
|
20
|
-
$ ARCHIL_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
21
|
-
$ npx @archildata/just-bash -r aws-us-east-1 -d myaccount/mydisk -l debug
|
|
33
|
+
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY S3 credentials for bucket mode
|
|
22
34
|
`);
|
|
23
35
|
program.parse();
|
|
24
36
|
var opts = program.opts();
|
|
25
37
|
var args = program.args;
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
console.error(" ARCHIL_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk");
|
|
38
|
-
console.error("\nRun with --help for more options.");
|
|
39
|
-
process.exit(1);
|
|
38
|
+
var isS3Bucket = args[0]?.startsWith("s3://");
|
|
39
|
+
function parseS3Url(url) {
|
|
40
|
+
const match = url.match(/^s3:\/\/([^/]+)(\/.*)?$/);
|
|
41
|
+
if (!match) {
|
|
42
|
+
throw new Error(`Invalid S3 URL: ${url}`);
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
bucket: match[1],
|
|
46
|
+
prefix: match[2]?.slice(1) || void 0
|
|
47
|
+
// Remove leading /
|
|
48
|
+
};
|
|
40
49
|
}
|
|
41
|
-
|
|
50
|
+
function diskNameFromBucket(bucket) {
|
|
51
|
+
return `s3-${bucket.replace(/[^a-zA-Z0-9-]/g, "-")}`;
|
|
52
|
+
}
|
|
53
|
+
function findDiskForBucket(disks, bucketName) {
|
|
54
|
+
return disks.find(
|
|
55
|
+
(disk) => disk.mounts?.some(
|
|
56
|
+
(mount) => mount.type === "s3" && mount.config?.bucketName === bucketName
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
async function runS3BucketMode() {
|
|
61
|
+
const s3Url = args[0];
|
|
62
|
+
const apiKey = opts.apiKey || process.env.ARCHIL_API_KEY;
|
|
63
|
+
const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;
|
|
64
|
+
const region = opts.region || process.env.ARCHIL_REGION || "aws-us-east-1";
|
|
65
|
+
const bucketRegion = opts.bucketRegion || "us-east-1";
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
console.error("Error: --api-key is required for S3 bucket mode");
|
|
68
|
+
console.error("");
|
|
69
|
+
console.error("Usage:");
|
|
70
|
+
console.error(" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx");
|
|
71
|
+
console.error("");
|
|
72
|
+
console.error("Or set ARCHIL_API_KEY environment variable:");
|
|
73
|
+
console.error(" ARCHIL_API_KEY=adt_xxx npx @archildata/just-bash s3://my-bucket");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
77
|
+
const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
78
|
+
if (!awsAccessKeyId || !awsSecretAccessKey) {
|
|
79
|
+
console.error("Error: AWS credentials required for S3 bucket mode");
|
|
80
|
+
console.error("");
|
|
81
|
+
console.error("Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables:");
|
|
82
|
+
console.error(" AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const { bucket, prefix } = parseS3Url(s3Url);
|
|
86
|
+
const combinedPrefix = opts.bucketPrefix ? opts.bucketPrefix + (prefix ? "/" + prefix : "") : prefix;
|
|
87
|
+
console.log("Archil S3 Quick Start");
|
|
88
|
+
console.log("=====================");
|
|
89
|
+
console.log(` Bucket: ${bucket}`);
|
|
90
|
+
if (combinedPrefix) {
|
|
91
|
+
console.log(` Prefix: ${combinedPrefix}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(` Region: ${region}`);
|
|
94
|
+
console.log(` Bucket Region: ${bucketRegion}`);
|
|
95
|
+
console.log("");
|
|
96
|
+
const archil = new Archil({
|
|
97
|
+
apiKey,
|
|
98
|
+
region
|
|
99
|
+
});
|
|
100
|
+
console.log("Checking for existing disk...");
|
|
101
|
+
let disk;
|
|
102
|
+
try {
|
|
103
|
+
const disks = await archil.disks.list();
|
|
104
|
+
disk = findDiskForBucket(disks, bucket);
|
|
105
|
+
if (disk) {
|
|
106
|
+
console.log(`Found existing disk: ${disk.name} (${disk.id})`);
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error("Failed to list disks:", err instanceof Error ? err.message : err);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
if (!disk) {
|
|
113
|
+
const diskName = diskNameFromBucket(bucket);
|
|
114
|
+
console.log(`Creating new disk: ${diskName}`);
|
|
115
|
+
try {
|
|
116
|
+
disk = await archil.disks.create({
|
|
117
|
+
name: diskName,
|
|
118
|
+
mounts: [
|
|
119
|
+
{
|
|
120
|
+
type: "s3",
|
|
121
|
+
bucketName: bucket,
|
|
122
|
+
bucketPrefix: combinedPrefix,
|
|
123
|
+
accessKeyId: awsAccessKeyId,
|
|
124
|
+
secretAccessKey: awsSecretAccessKey
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
console.log(`Created disk: ${disk.name} (${disk.id})`);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error("Failed to create disk:", err instanceof Error ? err.message : err);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
console.log("Connecting...");
|
|
135
|
+
let client;
|
|
136
|
+
try {
|
|
137
|
+
client = await disk.mount({ authToken: apiKey, logLevel });
|
|
138
|
+
console.log("Connected!");
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error("Failed to connect:", err instanceof Error ? err.message : err);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
return { client, diskName: disk.name, subdirectory: void 0 };
|
|
144
|
+
}
|
|
145
|
+
async function runDirectMode() {
|
|
146
|
+
const region = args[0] || opts.region || process.env.ARCHIL_REGION;
|
|
147
|
+
const rawDisk = args[1] || opts.disk || process.env.ARCHIL_DISK;
|
|
148
|
+
const authToken = opts.token || process.env.ARCHIL_TOKEN;
|
|
149
|
+
const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;
|
|
150
|
+
let diskName;
|
|
151
|
+
let subdirectory;
|
|
152
|
+
if (rawDisk) {
|
|
153
|
+
const colonIdx = rawDisk.indexOf(":/");
|
|
154
|
+
if (colonIdx !== -1) {
|
|
155
|
+
diskName = rawDisk.substring(0, colonIdx);
|
|
156
|
+
const subdir = rawDisk.substring(colonIdx + 1).replace(/^\/+|\/+$/g, "");
|
|
157
|
+
if (subdir) {
|
|
158
|
+
subdirectory = subdir;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
diskName = rawDisk;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!region || !diskName) {
|
|
165
|
+
console.error("Error: region and disk are required\n");
|
|
166
|
+
console.error("Usage:");
|
|
167
|
+
console.error(" npx @archildata/just-bash <region> <disk>");
|
|
168
|
+
console.error(" npx @archildata/just-bash --region <region> --disk <disk>");
|
|
169
|
+
console.error("\nQuick start with S3 bucket:");
|
|
170
|
+
console.error(" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx");
|
|
171
|
+
console.error("\nExamples:");
|
|
172
|
+
console.error(" npx @archildata/just-bash aws-us-east-1 myaccount/mydisk");
|
|
173
|
+
console.error(" ARCHIL_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk");
|
|
174
|
+
console.error("\nRun with --help for more options.");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
42
177
|
console.log("Connecting to Archil...");
|
|
43
178
|
console.log(` Region: ${region}`);
|
|
44
179
|
console.log(` Disk: ${diskName}`);
|
|
180
|
+
if (subdirectory) {
|
|
181
|
+
console.log(` Subdirectory: ${subdirectory}`);
|
|
182
|
+
}
|
|
45
183
|
console.log(` Auth: ${authToken ? "token" : "IAM"}`);
|
|
46
184
|
if (logLevel) {
|
|
47
185
|
console.log(` Log level: ${logLevel}`);
|
|
@@ -60,8 +198,12 @@ async function main() {
|
|
|
60
198
|
console.error("Failed to connect:", err instanceof Error ? err.message : err);
|
|
61
199
|
process.exit(1);
|
|
62
200
|
}
|
|
63
|
-
|
|
64
|
-
|
|
201
|
+
return { client, diskName, subdirectory };
|
|
202
|
+
}
|
|
203
|
+
async function main() {
|
|
204
|
+
const { client, diskName, subdirectory } = isS3Bucket ? await runS3BucketMode() : await runDirectMode();
|
|
205
|
+
const fs = await ArchilFs.create(client, { subdirectory });
|
|
206
|
+
const bash = new Bash({ fs, customCommands: [createArchilCommand(client, fs)] });
|
|
65
207
|
let cleaningUp = false;
|
|
66
208
|
const cleanup = async (signal) => {
|
|
67
209
|
if (cleaningUp) return;
|
|
@@ -87,6 +229,8 @@ Received ${signal}, cleaning up...`);
|
|
|
87
229
|
process.on("SIGHUP", () => cleanup("SIGHUP"));
|
|
88
230
|
console.log("");
|
|
89
231
|
console.log("=== Archil just-bash shell ===");
|
|
232
|
+
console.log(`Connected to: ${diskName}${subdirectory ? ":" + subdirectory : ""}`);
|
|
233
|
+
console.log("");
|
|
90
234
|
console.log("Type bash commands to interact with the filesystem.");
|
|
91
235
|
console.log("Special commands:");
|
|
92
236
|
console.log(" archil checkout [--force] <path> - Acquire write delegation");
|
|
@@ -106,82 +250,6 @@ Received ${signal}, cleaning up...`);
|
|
|
106
250
|
rl.prompt();
|
|
107
251
|
};
|
|
108
252
|
prompt();
|
|
109
|
-
const resolvePath = (path) => {
|
|
110
|
-
if (path.startsWith("/")) {
|
|
111
|
-
return path;
|
|
112
|
-
}
|
|
113
|
-
return cwd === "/" ? "/" + path : cwd + "/" + path;
|
|
114
|
-
};
|
|
115
|
-
const handleArchilCommand = async (command) => {
|
|
116
|
-
const parts = command.split(/\s+/);
|
|
117
|
-
if (parts[0] !== "archil") {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
const subcommand = parts[1];
|
|
121
|
-
const targetPath = parts[2];
|
|
122
|
-
if (subcommand === "checkout") {
|
|
123
|
-
const args2 = parts.slice(2);
|
|
124
|
-
const force = args2.includes("--force") || args2.includes("-f");
|
|
125
|
-
const pathArg = args2.find((a) => !a.startsWith("-"));
|
|
126
|
-
if (!pathArg) {
|
|
127
|
-
console.error("Usage: archil checkout [--force|-f] <path>");
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
const fullPath = resolvePath(pathArg);
|
|
131
|
-
try {
|
|
132
|
-
const inodeId = await fs.resolveInodeId(fullPath);
|
|
133
|
-
await client.checkout(inodeId, { force });
|
|
134
|
-
console.log(`Checked out: ${fullPath} (inode ${inodeId})${force ? " (forced)" : ""}`);
|
|
135
|
-
} catch (err) {
|
|
136
|
-
console.error(`Failed to checkout ${fullPath}:`, err instanceof Error ? err.message : err);
|
|
137
|
-
}
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
if (subcommand === "checkin") {
|
|
141
|
-
if (!targetPath) {
|
|
142
|
-
console.error("Usage: archil checkin <path>");
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
const fullPath = resolvePath(targetPath);
|
|
146
|
-
try {
|
|
147
|
-
const inodeId = await fs.resolveInodeId(fullPath);
|
|
148
|
-
await client.checkin(inodeId);
|
|
149
|
-
console.log(`Checked in: ${fullPath} (inode ${inodeId})`);
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.error(`Failed to checkin ${fullPath}:`, err instanceof Error ? err.message : err);
|
|
152
|
-
}
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
if (subcommand === "list-delegations" || subcommand === "delegations") {
|
|
156
|
-
try {
|
|
157
|
-
const delegations = client.listDelegations();
|
|
158
|
-
if (delegations.length === 0) {
|
|
159
|
-
console.log("No delegations held");
|
|
160
|
-
} else {
|
|
161
|
-
console.log("Delegations:");
|
|
162
|
-
for (const d of delegations) {
|
|
163
|
-
console.log(` inode ${d.inodeId}: ${d.state}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
} catch (err) {
|
|
167
|
-
console.error("Failed to list delegations:", err instanceof Error ? err.message : err);
|
|
168
|
-
}
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
if (subcommand === "help" || !subcommand) {
|
|
172
|
-
console.log("Archil commands:");
|
|
173
|
-
console.log(" archil checkout [--force|-f] <path> - Acquire write delegation");
|
|
174
|
-
console.log(" archil checkin <path> - Release write delegation");
|
|
175
|
-
console.log(" archil list-delegations - Show held delegations");
|
|
176
|
-
console.log(" archil help - Show this help message");
|
|
177
|
-
console.log("");
|
|
178
|
-
console.log("The --force flag revokes any existing delegation from other clients.");
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
console.error(`Unknown archil command: ${subcommand}`);
|
|
182
|
-
console.error("Run 'archil help' for available commands");
|
|
183
|
-
return true;
|
|
184
|
-
};
|
|
185
253
|
rl.on("line", async (line) => {
|
|
186
254
|
const trimmed = line.trim();
|
|
187
255
|
if (trimmed === "exit" || trimmed === "quit") {
|
|
@@ -194,12 +262,6 @@ Received ${signal}, cleaning up...`);
|
|
|
194
262
|
}
|
|
195
263
|
rl.pause();
|
|
196
264
|
try {
|
|
197
|
-
if (trimmed.startsWith("archil")) {
|
|
198
|
-
await handleArchilCommand(trimmed);
|
|
199
|
-
rl.resume();
|
|
200
|
-
prompt();
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
265
|
const result = await bash.exec(trimmed, {
|
|
204
266
|
cwd,
|
|
205
267
|
env: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archildata/just-bash",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Archil filesystem adapter for just-bash",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"shell": "tsx bin/shell.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@archildata/client": "^0.1
|
|
29
|
+
"@archildata/client": "^0.8.1",
|
|
30
30
|
"commander": "^14.0.3",
|
|
31
31
|
"debug": "^4.3.4",
|
|
32
32
|
"just-bash": "^2.7.0"
|