@archildata/just-bash 0.8.17 → 0.8.19
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/dist/index.cjs +633 -678
- package/dist/index.d.cts +131 -136
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +210 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/shell.mjs +319 -0
- package/dist/shell.mjs.map +1 -0
- package/dist/src-zhIdm_Vq.mjs +622 -0
- package/dist/src-zhIdm_Vq.mjs.map +1 -0
- package/package.json +20 -19
- package/dist/index.d.ts +0 -215
- package/dist/index.js +0 -652
- package/dist/shell.js +0 -313
package/dist/shell.mjs
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as createArchilCommand, r as ArchilFs } from "./src-zhIdm_Vq.mjs";
|
|
3
|
+
import { ArchilClient } from "@archildata/native";
|
|
4
|
+
import { Bash } from "just-bash";
|
|
5
|
+
import * as readline from "readline";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { Archil } from "disk";
|
|
8
|
+
//#region bin/shell.ts
|
|
9
|
+
/**
|
|
10
|
+
* Interactive just-bash shell with Archil filesystem
|
|
11
|
+
*
|
|
12
|
+
* Quick start with S3 bucket:
|
|
13
|
+
* npx @archildata/just-bash s3://my-bucket --api-key adt_xxx
|
|
14
|
+
*
|
|
15
|
+
* Traditional usage:
|
|
16
|
+
* npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
17
|
+
* npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk
|
|
18
|
+
*/
|
|
19
|
+
const program = new Command();
|
|
20
|
+
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", `
|
|
21
|
+
Quick Start (S3 bucket mode):
|
|
22
|
+
Connect directly to an S3 bucket - creates a disk if needed:
|
|
23
|
+
$ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx
|
|
24
|
+
$ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx --bucket-region us-west-2
|
|
25
|
+
|
|
26
|
+
AWS credentials are read from environment:
|
|
27
|
+
$ AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx
|
|
28
|
+
|
|
29
|
+
Traditional usage (direct connection):
|
|
30
|
+
$ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
31
|
+
$ npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk
|
|
32
|
+
$ ARCHIL_DISK_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk
|
|
33
|
+
|
|
34
|
+
Subdirectory mounting (mount a subdirectory as the root):
|
|
35
|
+
$ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk:/data/project
|
|
36
|
+
|
|
37
|
+
Environment variables:
|
|
38
|
+
ARCHIL_API_KEY API key for disk management
|
|
39
|
+
ARCHIL_REGION Fallback for --region
|
|
40
|
+
ARCHIL_DISK Fallback for --disk
|
|
41
|
+
ARCHIL_DISK_TOKEN Fallback for --token (recommended for secrets)
|
|
42
|
+
ARCHIL_LOG_LEVEL Fallback for --log-level
|
|
43
|
+
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY S3 credentials for bucket mode
|
|
44
|
+
`);
|
|
45
|
+
program.parse();
|
|
46
|
+
const opts = program.opts();
|
|
47
|
+
const args = program.args;
|
|
48
|
+
const isS3Bucket = args[0]?.startsWith("s3://");
|
|
49
|
+
/**
|
|
50
|
+
* Parse S3 bucket URL
|
|
51
|
+
*/
|
|
52
|
+
function parseS3Url(url) {
|
|
53
|
+
const match = url.match(/^s3:\/\/([^/]+)(\/.*)?$/);
|
|
54
|
+
if (!match) throw new Error(`Invalid S3 URL: ${url}`);
|
|
55
|
+
return {
|
|
56
|
+
bucket: match[1],
|
|
57
|
+
prefix: match[2]?.slice(1) || void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generate a disk name from an S3 bucket
|
|
62
|
+
*/
|
|
63
|
+
function diskNameFromBucket(bucket) {
|
|
64
|
+
return `s3-${bucket.replace(/[^a-zA-Z0-9-]/g, "-")}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Find an existing disk that mounts the given S3 bucket
|
|
68
|
+
*/
|
|
69
|
+
function findDiskForBucket(disks, bucketName) {
|
|
70
|
+
return disks.find((disk) => disk.mounts?.some((mount) => mount.type === "s3" && mount.config?.bucketName === bucketName));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* S3 bucket quick-start mode
|
|
74
|
+
*/
|
|
75
|
+
async function runS3BucketMode() {
|
|
76
|
+
const s3Url = args[0];
|
|
77
|
+
const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;
|
|
78
|
+
const region = opts.region || process.env.ARCHIL_REGION || "aws-us-east-1";
|
|
79
|
+
const bucketRegion = opts.bucketRegion || "us-east-1";
|
|
80
|
+
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
81
|
+
const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
82
|
+
if (!awsAccessKeyId || !awsSecretAccessKey) {
|
|
83
|
+
console.error("Error: AWS credentials required for S3 bucket mode");
|
|
84
|
+
console.error("");
|
|
85
|
+
console.error("Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables:");
|
|
86
|
+
console.error(" AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const { bucket, prefix } = parseS3Url(s3Url);
|
|
90
|
+
const combinedPrefix = opts.bucketPrefix ? opts.bucketPrefix + (prefix ? "/" + prefix : "") : prefix;
|
|
91
|
+
console.log("Archil S3 Quick Start");
|
|
92
|
+
console.log("=====================");
|
|
93
|
+
console.log(` Bucket: ${bucket}`);
|
|
94
|
+
if (combinedPrefix) console.log(` Prefix: ${combinedPrefix}`);
|
|
95
|
+
console.log(` Region: ${region}`);
|
|
96
|
+
console.log(` Bucket Region: ${bucketRegion}`);
|
|
97
|
+
console.log("");
|
|
98
|
+
let archil;
|
|
99
|
+
try {
|
|
100
|
+
archil = new Archil({
|
|
101
|
+
apiKey: opts.apiKey,
|
|
102
|
+
region
|
|
103
|
+
});
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error(err instanceof Error ? err.message : err);
|
|
106
|
+
console.error("");
|
|
107
|
+
console.error("Usage:");
|
|
108
|
+
console.error(" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx");
|
|
109
|
+
console.error("");
|
|
110
|
+
console.error("Or set ARCHIL_API_KEY environment variable:");
|
|
111
|
+
console.error(" ARCHIL_API_KEY=adt_xxx npx @archildata/just-bash s3://my-bucket");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log("Checking for existing disk...");
|
|
115
|
+
let disk;
|
|
116
|
+
let mountToken;
|
|
117
|
+
try {
|
|
118
|
+
disk = findDiskForBucket(await archil.disks.list(), bucket);
|
|
119
|
+
if (disk) console.log(`Found existing disk: ${disk.name} (${disk.id})`);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.error("Failed to list disks:", err instanceof Error ? err.message : err);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
if (!disk) {
|
|
125
|
+
const diskName = diskNameFromBucket(bucket);
|
|
126
|
+
console.log(`Creating new disk: ${diskName}`);
|
|
127
|
+
try {
|
|
128
|
+
const result = await archil.disks.create({
|
|
129
|
+
name: diskName,
|
|
130
|
+
mounts: [{
|
|
131
|
+
type: "s3",
|
|
132
|
+
bucketName: bucket,
|
|
133
|
+
bucketPrefix: combinedPrefix,
|
|
134
|
+
accessKeyId: awsAccessKeyId,
|
|
135
|
+
secretAccessKey: awsSecretAccessKey
|
|
136
|
+
}]
|
|
137
|
+
});
|
|
138
|
+
disk = result.disk;
|
|
139
|
+
mountToken = result.token ?? void 0;
|
|
140
|
+
console.log(`Created disk: ${disk.name} (${disk.id})`);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error("Failed to create disk:", err instanceof Error ? err.message : err);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!mountToken) try {
|
|
147
|
+
const { token } = await disk.createToken("just-bash");
|
|
148
|
+
mountToken = token;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error("Failed to create mount token:", err instanceof Error ? err.message : err);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
console.log("Connecting...");
|
|
154
|
+
let client;
|
|
155
|
+
try {
|
|
156
|
+
client = await disk.mount({
|
|
157
|
+
authToken: mountToken,
|
|
158
|
+
logLevel
|
|
159
|
+
});
|
|
160
|
+
console.log("Connected!");
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error("Failed to connect:", err instanceof Error ? err.message : err);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
client,
|
|
167
|
+
diskName: disk.name,
|
|
168
|
+
subdirectory: void 0
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Traditional direct connection mode
|
|
173
|
+
*/
|
|
174
|
+
async function runDirectMode() {
|
|
175
|
+
const region = args[0] || opts.region || process.env.ARCHIL_REGION;
|
|
176
|
+
const rawDisk = args[1] || opts.disk || process.env.ARCHIL_DISK;
|
|
177
|
+
const authToken = opts.token || process.env.ARCHIL_DISK_TOKEN;
|
|
178
|
+
const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;
|
|
179
|
+
let diskName;
|
|
180
|
+
let subdirectory;
|
|
181
|
+
if (rawDisk) {
|
|
182
|
+
const colonIdx = rawDisk.indexOf(":/");
|
|
183
|
+
if (colonIdx !== -1) {
|
|
184
|
+
diskName = rawDisk.substring(0, colonIdx);
|
|
185
|
+
const subdir = rawDisk.substring(colonIdx + 1).replace(/^\/+|\/+$/g, "");
|
|
186
|
+
if (subdir) subdirectory = subdir;
|
|
187
|
+
} else diskName = rawDisk;
|
|
188
|
+
}
|
|
189
|
+
if (!region || !diskName) {
|
|
190
|
+
console.error("Error: region and disk are required\n");
|
|
191
|
+
console.error("Usage:");
|
|
192
|
+
console.error(" npx @archildata/just-bash <region> <disk>");
|
|
193
|
+
console.error(" npx @archildata/just-bash --region <region> --disk <disk>");
|
|
194
|
+
console.error("\nQuick start with S3 bucket:");
|
|
195
|
+
console.error(" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx");
|
|
196
|
+
console.error("\nExamples:");
|
|
197
|
+
console.error(" npx @archildata/just-bash aws-us-east-1 myaccount/mydisk");
|
|
198
|
+
console.error(" ARCHIL_DISK_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk");
|
|
199
|
+
console.error("\nRun with --help for more options.");
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
console.log("Connecting to Archil...");
|
|
203
|
+
console.log(` Region: ${region}`);
|
|
204
|
+
console.log(` Disk: ${diskName}`);
|
|
205
|
+
if (subdirectory) console.log(` Subdirectory: ${subdirectory}`);
|
|
206
|
+
console.log(` Auth: ${authToken ? "token" : "IAM"}`);
|
|
207
|
+
if (logLevel) console.log(` Log level: ${logLevel}`);
|
|
208
|
+
console.log("");
|
|
209
|
+
let client;
|
|
210
|
+
try {
|
|
211
|
+
client = await ArchilClient.connect({
|
|
212
|
+
region,
|
|
213
|
+
diskName,
|
|
214
|
+
authToken: authToken || void 0,
|
|
215
|
+
logLevel
|
|
216
|
+
});
|
|
217
|
+
console.log("Connected!");
|
|
218
|
+
} catch (err) {
|
|
219
|
+
console.error("Failed to connect:", err instanceof Error ? err.message : err);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
client,
|
|
224
|
+
diskName,
|
|
225
|
+
subdirectory
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function main() {
|
|
229
|
+
const { client, diskName, subdirectory } = isS3Bucket ? await runS3BucketMode() : await runDirectMode();
|
|
230
|
+
const fs = await ArchilFs.create(client, { subdirectory });
|
|
231
|
+
const bash = new Bash({
|
|
232
|
+
fs,
|
|
233
|
+
customCommands: [createArchilCommand(client, fs)]
|
|
234
|
+
});
|
|
235
|
+
let cleaningUp = false;
|
|
236
|
+
const cleanup = async (signal) => {
|
|
237
|
+
if (cleaningUp) return;
|
|
238
|
+
cleaningUp = true;
|
|
239
|
+
if (signal) console.log(`\nReceived ${signal}, cleaning up...`);
|
|
240
|
+
else console.log("\nGoodbye!");
|
|
241
|
+
try {
|
|
242
|
+
const released = await client.close();
|
|
243
|
+
if (released > 0) console.log(`Released ${released} delegation${released > 1 ? "s" : ""}`);
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error("Error during cleanup:", err instanceof Error ? err.message : err);
|
|
246
|
+
}
|
|
247
|
+
process.exit(0);
|
|
248
|
+
};
|
|
249
|
+
process.on("SIGINT", () => cleanup("SIGINT"));
|
|
250
|
+
process.on("SIGTERM", () => cleanup("SIGTERM"));
|
|
251
|
+
process.on("SIGHUP", () => cleanup("SIGHUP"));
|
|
252
|
+
console.log("");
|
|
253
|
+
console.log("=== Archil just-bash shell ===");
|
|
254
|
+
console.log(`Connected to: ${diskName}${subdirectory ? ":" + subdirectory : ""}`);
|
|
255
|
+
console.log("");
|
|
256
|
+
console.log("Type bash commands to interact with the filesystem.");
|
|
257
|
+
console.log("Special commands:");
|
|
258
|
+
console.log(" archil checkout [--force] <path> - Acquire write delegation");
|
|
259
|
+
console.log(" archil checkin <path> - Release write delegation");
|
|
260
|
+
console.log(" archil list-delegations - Show held delegations");
|
|
261
|
+
console.log(" archil help - Show archil commands");
|
|
262
|
+
console.log("Type 'exit' or Ctrl+D to quit.");
|
|
263
|
+
console.log("");
|
|
264
|
+
const rl = readline.createInterface({
|
|
265
|
+
input: process.stdin,
|
|
266
|
+
output: process.stdout,
|
|
267
|
+
prompt: "archil$ "
|
|
268
|
+
});
|
|
269
|
+
let cwd = "/";
|
|
270
|
+
const prompt = () => {
|
|
271
|
+
rl.setPrompt(`archil:${cwd}$ `);
|
|
272
|
+
rl.prompt();
|
|
273
|
+
};
|
|
274
|
+
prompt();
|
|
275
|
+
rl.on("line", async (line) => {
|
|
276
|
+
const trimmed = line.trim();
|
|
277
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
278
|
+
await cleanup();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!trimmed) {
|
|
282
|
+
prompt();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
rl.pause();
|
|
286
|
+
try {
|
|
287
|
+
const result = await bash.exec(trimmed, {
|
|
288
|
+
cwd,
|
|
289
|
+
env: {
|
|
290
|
+
HOME: "/",
|
|
291
|
+
USER: "archil",
|
|
292
|
+
PWD: cwd
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
if (result.stdout) {
|
|
296
|
+
process.stdout.write(result.stdout);
|
|
297
|
+
if (!result.stdout.endsWith("\n")) console.log("");
|
|
298
|
+
}
|
|
299
|
+
if (result.stderr) {
|
|
300
|
+
process.stderr.write(result.stderr);
|
|
301
|
+
if (!result.stderr.endsWith("\n")) console.error("");
|
|
302
|
+
}
|
|
303
|
+
if (result.env?.PWD && result.env.PWD !== cwd) cwd = result.env.PWD;
|
|
304
|
+
} catch (err) {
|
|
305
|
+
console.error("Error:", err instanceof Error ? err.message : err);
|
|
306
|
+
}
|
|
307
|
+
rl.resume();
|
|
308
|
+
prompt();
|
|
309
|
+
});
|
|
310
|
+
rl.on("close", () => cleanup());
|
|
311
|
+
}
|
|
312
|
+
main().catch((err) => {
|
|
313
|
+
console.error("Fatal error:", err);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
});
|
|
316
|
+
//#endregion
|
|
317
|
+
export {};
|
|
318
|
+
|
|
319
|
+
//# sourceMappingURL=shell.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.mjs","names":[],"sources":["../bin/shell.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Interactive just-bash shell with Archil filesystem\n *\n * Quick start with S3 bucket:\n * npx @archildata/just-bash s3://my-bucket --api-key adt_xxx\n *\n * Traditional usage:\n * npx @archildata/just-bash aws-us-east-1 myaccount/mydisk\n * npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk\n */\n\nimport * as readline from \"readline\";\nimport { Command } from \"commander\";\nimport { ArchilClient } from \"@archildata/native\";\nimport { Archil, Disk } from \"disk\";\nimport { Bash } from \"just-bash\";\nimport { ArchilFs, createArchilCommand } from \"../src/index.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"archil-shell\")\n .description(\"Interactive bash shell with Archil filesystem\")\n .version(\"0.1.0\")\n .argument(\"[target]\", \"S3 bucket (s3://bucket) or region (e.g., aws-us-east-1)\")\n .argument(\"[disk]\", \"Disk name (e.g., myaccount/mydisk) - only when target is region\")\n .option(\"-r, --region <region>\", \"Region identifier (e.g., aws-us-east-1)\")\n .option(\"-d, --disk <disk>\", \"Disk name (e.g., myaccount/mydisk)\")\n .option(\"-k, --api-key <key>\", \"API key for disk management (required for S3 bucket mode)\")\n .option(\"-t, --token <token>\", \"Auth token for direct connection (defaults to IAM)\")\n .option(\"-l, --log-level <level>\", \"Log level: trace, debug, info, warn, error\")\n .option(\"--bucket-region <region>\", \"AWS region for the S3 bucket (default: us-east-1)\")\n .option(\"--bucket-prefix <prefix>\", \"Path prefix within the bucket\")\n .addHelpText(\"after\", `\nQuick Start (S3 bucket mode):\n Connect directly to an S3 bucket - creates a disk if needed:\n $ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx\n $ npx @archildata/just-bash s3://my-bucket --api-key adt_xxx --bucket-region us-west-2\n\n AWS credentials are read from environment:\n $ AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx\n\nTraditional usage (direct connection):\n $ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk\n $ npx @archildata/just-bash --region aws-us-east-1 --disk myaccount/mydisk\n $ ARCHIL_DISK_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk\n\nSubdirectory mounting (mount a subdirectory as the root):\n $ npx @archildata/just-bash aws-us-east-1 myaccount/mydisk:/data/project\n\nEnvironment variables:\n ARCHIL_API_KEY API key for disk management\n ARCHIL_REGION Fallback for --region\n ARCHIL_DISK Fallback for --disk\n ARCHIL_DISK_TOKEN Fallback for --token (recommended for secrets)\n ARCHIL_LOG_LEVEL Fallback for --log-level\n AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY S3 credentials for bucket mode\n`);\n\nprogram.parse();\n\nconst opts = program.opts();\nconst args = program.args;\n\n// Check if first argument is an S3 bucket\nconst isS3Bucket = args[0]?.startsWith(\"s3://\");\n\n/**\n * Parse S3 bucket URL\n */\nfunction parseS3Url(url: string): { bucket: string; prefix?: string } {\n const match = url.match(/^s3:\\/\\/([^/]+)(\\/.*)?$/);\n if (!match) {\n throw new Error(`Invalid S3 URL: ${url}`);\n }\n return {\n bucket: match[1],\n prefix: match[2]?.slice(1) || undefined, // Remove leading /\n };\n}\n\n/**\n * Generate a disk name from an S3 bucket\n */\nfunction diskNameFromBucket(bucket: string): string {\n // Sanitize bucket name for use as disk name (alphanumeric, hyphens, underscores)\n return `s3-${bucket.replace(/[^a-zA-Z0-9-]/g, \"-\")}`;\n}\n\n/**\n * Find an existing disk that mounts the given S3 bucket\n */\nfunction findDiskForBucket(disks: Disk[], bucketName: string): Disk | undefined {\n return disks.find((disk) =>\n disk.mounts?.some(\n (mount) =>\n mount.type === \"s3\" &&\n mount.config?.bucketName === bucketName\n )\n );\n}\n\n/**\n * S3 bucket quick-start mode\n */\nasync function runS3BucketMode() {\n const s3Url = args[0];\n const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;\n const region = opts.region || process.env.ARCHIL_REGION || \"aws-us-east-1\";\n const bucketRegion = opts.bucketRegion || \"us-east-1\";\n\n // Check for AWS credentials\n const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;\n const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;\n\n if (!awsAccessKeyId || !awsSecretAccessKey) {\n console.error(\"Error: AWS credentials required for S3 bucket mode\");\n console.error(\"\");\n console.error(\"Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables:\");\n console.error(\" AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx npx @archildata/just-bash s3://my-bucket -k adt_xxx\");\n process.exit(1);\n }\n\n const { bucket, prefix } = parseS3Url(s3Url);\n const combinedPrefix = opts.bucketPrefix\n ? opts.bucketPrefix + (prefix ? \"/\" + prefix : \"\")\n : prefix;\n\n console.log(\"Archil S3 Quick Start\");\n console.log(\"=====================\");\n console.log(` Bucket: ${bucket}`);\n if (combinedPrefix) {\n console.log(` Prefix: ${combinedPrefix}`);\n }\n console.log(` Region: ${region}`);\n console.log(` Bucket Region: ${bucketRegion}`);\n console.log(\"\");\n\n // Initialize Archil control plane client\n // apiKey falls back to ARCHIL_API_KEY env var via the Archil constructor\n let archil: Archil;\n try {\n archil = new Archil({\n apiKey: opts.apiKey,\n region,\n });\n } catch (err) {\n console.error(err instanceof Error ? err.message : err);\n console.error(\"\");\n console.error(\"Usage:\");\n console.error(\" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx\");\n console.error(\"\");\n console.error(\"Or set ARCHIL_API_KEY environment variable:\");\n console.error(\" ARCHIL_API_KEY=adt_xxx npx @archildata/just-bash s3://my-bucket\");\n process.exit(1);\n }\n\n // Check if a disk for this bucket already exists\n console.log(\"Checking for existing disk...\");\n let disk: Disk | undefined;\n let mountToken: string | undefined;\n\n try {\n const disks = await archil.disks.list();\n disk = findDiskForBucket(disks, bucket);\n\n if (disk) {\n console.log(`Found existing disk: ${disk.name} (${disk.id})`);\n }\n } catch (err) {\n console.error(\"Failed to list disks:\", err instanceof Error ? err.message : err);\n process.exit(1);\n }\n\n // Create disk if it doesn't exist\n if (!disk) {\n const diskName = diskNameFromBucket(bucket);\n console.log(`Creating new disk: ${diskName}`);\n\n try {\n const result = await archil.disks.create({\n name: diskName,\n mounts: [\n {\n type: \"s3\",\n bucketName: bucket,\n bucketPrefix: combinedPrefix,\n accessKeyId: awsAccessKeyId,\n secretAccessKey: awsSecretAccessKey,\n },\n ],\n });\n disk = result.disk;\n mountToken = result.token ?? undefined;\n console.log(`Created disk: ${disk.name} (${disk.id})`);\n } catch (err) {\n console.error(\"Failed to create disk:\", err instanceof Error ? err.message : err);\n process.exit(1);\n }\n }\n\n // For existing disks, generate a fresh mount token\n if (!mountToken) {\n try {\n const { token } = await disk.createToken(\"just-bash\");\n mountToken = token;\n } catch (err) {\n console.error(\"Failed to create mount token:\", err instanceof Error ? err.message : err);\n process.exit(1);\n }\n }\n\n // Connect to the disk\n console.log(\"Connecting...\");\n let client: ArchilClient;\n try {\n client = await disk.mount({ authToken: mountToken, logLevel }) as ArchilClient;\n console.log(\"Connected!\");\n } catch (err) {\n console.error(\"Failed to connect:\", err instanceof Error ? err.message : err);\n process.exit(1);\n }\n\n return { client, diskName: disk.name, subdirectory: undefined as string | undefined };\n}\n\n/**\n * Traditional direct connection mode\n */\nasync function runDirectMode() {\n const region = args[0] || opts.region || process.env.ARCHIL_REGION;\n const rawDisk = args[1] || opts.disk || process.env.ARCHIL_DISK;\n const authToken = opts.token || process.env.ARCHIL_DISK_TOKEN;\n const logLevel = opts.logLevel || process.env.ARCHIL_LOG_LEVEL;\n\n // Parse disk:/subdirectory syntax (e.g., \"myaccount/mydisk:/data/project\")\n // Uses \":/\" delimiter to match NFS convention where paths are always absolute.\n let diskName: string | undefined;\n let subdirectory: string | undefined;\n if (rawDisk) {\n const colonIdx = rawDisk.indexOf(\":/\");\n if (colonIdx !== -1) {\n diskName = rawDisk.substring(0, colonIdx);\n const subdir = rawDisk.substring(colonIdx + 1).replace(/^\\/+|\\/+$/g, \"\");\n if (subdir) {\n subdirectory = subdir;\n }\n } else {\n diskName = rawDisk;\n }\n }\n\n if (!region || !diskName) {\n console.error(\"Error: region and disk are required\\n\");\n console.error(\"Usage:\");\n console.error(\" npx @archildata/just-bash <region> <disk>\");\n console.error(\" npx @archildata/just-bash --region <region> --disk <disk>\");\n console.error(\"\\nQuick start with S3 bucket:\");\n console.error(\" npx @archildata/just-bash s3://my-bucket --api-key adt_xxx\");\n console.error(\"\\nExamples:\");\n console.error(\" npx @archildata/just-bash aws-us-east-1 myaccount/mydisk\");\n console.error(\" ARCHIL_DISK_TOKEN=xxx npx @archildata/just-bash aws-us-east-1 myaccount/mydisk\");\n console.error(\"\\nRun with --help for more options.\");\n process.exit(1);\n }\n\n console.log(\"Connecting to Archil...\");\n console.log(` Region: ${region}`);\n console.log(` Disk: ${diskName}`);\n if (subdirectory) {\n console.log(` Subdirectory: ${subdirectory}`);\n }\n console.log(` Auth: ${authToken ? \"token\" : \"IAM\"}`);\n if (logLevel) {\n console.log(` Log level: ${logLevel}`);\n }\n console.log(\"\");\n\n let client: ArchilClient;\n try {\n client = await ArchilClient.connect({\n region,\n diskName,\n authToken: authToken || undefined,\n logLevel,\n });\n console.log(\"Connected!\");\n } catch (err) {\n console.error(\"Failed to connect:\", err instanceof Error ? err.message : err);\n process.exit(1);\n }\n\n return { client, diskName, subdirectory };\n}\n\nasync function main() {\n // Determine which mode to run\n const { client, diskName, subdirectory } = isS3Bucket\n ? await runS3BucketMode()\n : await runDirectMode();\n\n // Create filesystem adapter (resolves subdirectory eagerly if specified)\n const fs = await ArchilFs.create(client, { subdirectory });\n\n // Create bash environment with archil delegation commands\n const bash = new Bash({ fs, customCommands: [createArchilCommand(client, fs)] });\n\n // Cleanup function to release delegations and close connection\n let cleaningUp = false;\n const cleanup = async (signal?: string) => {\n if (cleaningUp) return;\n cleaningUp = true;\n\n if (signal) {\n console.log(`\\nReceived ${signal}, cleaning up...`);\n } else {\n console.log(\"\\nGoodbye!\");\n }\n\n try {\n // close() releases all delegations and cleans up resources\n const released = await client.close();\n if (released > 0) {\n console.log(`Released ${released} delegation${released > 1 ? \"s\" : \"\"}`);\n }\n } catch (err) {\n console.error(\"Error during cleanup:\", err instanceof Error ? err.message : err);\n }\n\n process.exit(0);\n };\n\n // Handle signals for graceful shutdown\n process.on(\"SIGINT\", () => cleanup(\"SIGINT\"));\n process.on(\"SIGTERM\", () => cleanup(\"SIGTERM\"));\n process.on(\"SIGHUP\", () => cleanup(\"SIGHUP\"));\n\n console.log(\"\");\n console.log(\"=== Archil just-bash shell ===\");\n console.log(`Connected to: ${diskName}${subdirectory ? \":\" + subdirectory : \"\"}`);\n console.log(\"\");\n console.log(\"Type bash commands to interact with the filesystem.\");\n console.log(\"Special commands:\");\n console.log(\" archil checkout [--force] <path> - Acquire write delegation\");\n console.log(\" archil checkin <path> - Release write delegation\");\n console.log(\" archil list-delegations - Show held delegations\");\n console.log(\" archil help - Show archil commands\");\n console.log(\"Type 'exit' or Ctrl+D to quit.\");\n console.log(\"\");\n\n // Create readline interface for interactive input\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n prompt: \"archil$ \",\n });\n\n let cwd = \"/\";\n\n const prompt = () => {\n rl.setPrompt(`archil:${cwd}$ `);\n rl.prompt();\n };\n\n prompt();\n\n rl.on(\"line\", async (line) => {\n const trimmed = line.trim();\n\n if (trimmed === \"exit\" || trimmed === \"quit\") {\n await cleanup();\n return;\n }\n\n if (!trimmed) {\n prompt();\n return;\n }\n\n // Pause readline while we process the command\n rl.pause();\n\n try {\n const result = await bash.exec(trimmed, {\n cwd,\n env: {\n HOME: \"/\",\n USER: \"archil\",\n PWD: cwd,\n },\n });\n\n // Print output\n if (result.stdout) {\n process.stdout.write(result.stdout);\n if (!result.stdout.endsWith(\"\\n\")) {\n console.log(\"\");\n }\n }\n if (result.stderr) {\n process.stderr.write(result.stderr);\n if (!result.stderr.endsWith(\"\\n\")) {\n console.error(\"\");\n }\n }\n\n // Update cwd if command changed it\n if (result.env?.PWD && result.env.PWD !== cwd) {\n cwd = result.env.PWD;\n }\n } catch (err) {\n console.error(\"Error:\", err instanceof Error ? err.message : err);\n }\n\n rl.resume();\n prompt();\n });\n\n rl.on(\"close\", () => cleanup());\n}\n\nmain().catch((err) => {\n console.error(\"Fatal error:\", err);\n process.exit(1);\n});\n"],"mappings":"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"}
|