@specific.dev/cli 0.1.143 → 0.1.146
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/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +4 -4
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +7 -7
- package/dist/admin/__next._full.txt +16 -16
- package/dist/admin/__next._head.txt +4 -4
- package/dist/admin/__next._index.txt +6 -6
- package/dist/admin/__next._tree.txt +2 -2
- package/dist/admin/_next/static/chunks/4894dd6189acd52e.js +1 -0
- package/dist/admin/_next/static/chunks/48cbaceebda5df57.js +1 -0
- package/dist/admin/_next/static/chunks/6d4c1852135da048.js +5 -0
- package/dist/admin/_next/static/chunks/{a308451471d4cb39.js → 7c2bbd09e9922443.js} +1 -1
- package/dist/admin/_next/static/chunks/7e5c5b91b30c68a0.js +7 -0
- package/dist/admin/_next/static/chunks/9835cc4e14c5aee3.js +1 -0
- package/dist/admin/_next/static/chunks/b44581875c51fcd8.js +1 -0
- package/dist/admin/_next/static/chunks/b6749b7e46ea7447.css +4 -0
- package/dist/admin/_next/static/chunks/b69f189d24b42cc4.js +1 -0
- package/dist/admin/_next/static/chunks/c52b2ebc474a0535.js +1 -0
- package/dist/admin/_next/static/chunks/c82a52b3c62e7d18.js +1 -0
- package/dist/admin/_next/static/chunks/dc5c264e3262a84a.js +1 -0
- package/dist/admin/_not-found/__next._full.txt +11 -11
- package/dist/admin/_not-found/__next._head.txt +4 -4
- package/dist/admin/_not-found/__next._index.txt +6 -6
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/admin/_not-found/__next._not-found.txt +3 -3
- package/dist/admin/_not-found/__next._tree.txt +2 -2
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +11 -11
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +4 -4
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +3 -3
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +7 -7
- package/dist/admin/databases/__next._full.txt +16 -16
- package/dist/admin/databases/__next._head.txt +4 -4
- package/dist/admin/databases/__next._index.txt +6 -6
- package/dist/admin/databases/__next._tree.txt +2 -2
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +16 -16
- package/dist/admin/fullscreen/__next._full.txt +12 -12
- package/dist/admin/fullscreen/__next._head.txt +4 -4
- package/dist/admin/fullscreen/__next._index.txt +6 -6
- package/dist/admin/fullscreen/__next._tree.txt +2 -2
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +4 -4
- package/dist/admin/fullscreen/__next.fullscreen.txt +3 -3
- package/dist/admin/fullscreen/databases/__next._full.txt +12 -12
- package/dist/admin/fullscreen/databases/__next._head.txt +4 -4
- package/dist/admin/fullscreen/databases/__next._index.txt +6 -6
- package/dist/admin/fullscreen/databases/__next._tree.txt +2 -2
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +4 -4
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +3 -3
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +3 -3
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +12 -12
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +12 -12
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +16 -16
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +4 -4
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +3 -3
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +7 -7
- package/dist/admin/mail/__next._full.txt +16 -16
- package/dist/admin/mail/__next._head.txt +4 -4
- package/dist/admin/mail/__next._index.txt +6 -6
- package/dist/admin/mail/__next._tree.txt +2 -2
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +16 -16
- package/dist/admin/services/__next.!KGRlZmF1bHQp.services.__PAGE__.txt +4 -4
- package/dist/admin/services/__next.!KGRlZmF1bHQp.services.txt +3 -3
- package/dist/admin/services/__next.!KGRlZmF1bHQp.txt +7 -7
- package/dist/admin/services/__next._full.txt +16 -16
- package/dist/admin/services/__next._head.txt +4 -4
- package/dist/admin/services/__next._index.txt +6 -6
- package/dist/admin/services/__next._tree.txt +2 -2
- package/dist/admin/services/index.html +1 -1
- package/dist/admin/services/index.txt +16 -16
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.storage.__PAGE__.txt +9 -0
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.storage.txt +4 -0
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.txt +8 -0
- package/dist/admin/storage/__next._full.txt +27 -0
- package/dist/admin/storage/__next._head.txt +6 -0
- package/dist/admin/storage/__next._index.txt +7 -0
- package/dist/admin/storage/__next._tree.txt +5 -0
- package/dist/admin/storage/index.html +1 -0
- package/dist/admin/storage/index.txt +27 -0
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +7 -7
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +4 -4
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +3 -3
- package/dist/admin/workflows/__next._full.txt +16 -16
- package/dist/admin/workflows/__next._head.txt +4 -4
- package/dist/admin/workflows/__next._index.txt +6 -6
- package/dist/admin/workflows/__next._tree.txt +2 -2
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +16 -16
- package/dist/cli.js +488 -24
- package/dist/docs/redis.md +1 -1
- package/package.json +2 -1
- package/dist/admin/_next/static/chunks/29131741af270a46.js +0 -1
- package/dist/admin/_next/static/chunks/4e5f2d0efc90ca9f.js +0 -5
- package/dist/admin/_next/static/chunks/562ce7f5a20cc20f.js +0 -1
- package/dist/admin/_next/static/chunks/63accf2b484df522.js +0 -1
- package/dist/admin/_next/static/chunks/7058ba3be99e5848.js +0 -1
- package/dist/admin/_next/static/chunks/969af33935e3a7d5.css +0 -4
- package/dist/admin/_next/static/chunks/c0853089f2cd9ccb.js +0 -1
- package/dist/admin/_next/static/chunks/db49f609ddd8888b.js +0 -1
- package/dist/admin/_next/static/chunks/e4ac6f714e409140.js +0 -1
- /package/dist/admin/_next/static/{D-FFm4sI-NGxWLJmxd3IX → g6SjZi_LuWK7FrmmMr5PE}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{D-FFm4sI-NGxWLJmxd3IX → g6SjZi_LuWK7FrmmMr5PE}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{D-FFm4sI-NGxWLJmxd3IX → g6SjZi_LuWK7FrmmMr5PE}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -185198,6 +185198,16 @@ import * as http from "http";
|
|
|
185198
185198
|
import * as fs7 from "fs";
|
|
185199
185199
|
import * as path7 from "path";
|
|
185200
185200
|
import { fileURLToPath } from "url";
|
|
185201
|
+
import {
|
|
185202
|
+
CopyObjectCommand,
|
|
185203
|
+
DeleteObjectCommand,
|
|
185204
|
+
DeleteObjectsCommand,
|
|
185205
|
+
GetObjectCommand,
|
|
185206
|
+
HeadObjectCommand,
|
|
185207
|
+
ListObjectsV2Command,
|
|
185208
|
+
PutObjectCommand,
|
|
185209
|
+
S3Client
|
|
185210
|
+
} from "@aws-sdk/client-s3";
|
|
185201
185211
|
import * as net3 from "net";
|
|
185202
185212
|
import * as fs8 from "fs";
|
|
185203
185213
|
import * as path9 from "path";
|
|
@@ -369113,9 +369123,9 @@ var postgresBinary = {
|
|
|
369113
369123
|
};
|
|
369114
369124
|
var redisBinary = {
|
|
369115
369125
|
name: "redis",
|
|
369116
|
-
versions: ["8.
|
|
369126
|
+
versions: ["8.1.7"],
|
|
369117
369127
|
// URL: binaries.specific.dev/{SOFTWARE}/{VERSION}/{ARCH}.tar.gz
|
|
369118
|
-
urlTemplate: "https://binaries.specific.dev/
|
|
369128
|
+
urlTemplate: "https://binaries.specific.dev/valkey/{version}/{arch}.tar.gz",
|
|
369119
369129
|
platformMapping: {
|
|
369120
369130
|
darwin: {
|
|
369121
369131
|
arm64: { platform: "macos", arch: "macos_arm" },
|
|
@@ -369128,8 +369138,7 @@ var redisBinary = {
|
|
|
369128
369138
|
},
|
|
369129
369139
|
// Binaries are at the root of the archive
|
|
369130
369140
|
stripComponents: 0,
|
|
369131
|
-
|
|
369132
|
-
executables: ["redis-server"]
|
|
369141
|
+
executables: ["valkey-server"]
|
|
369133
369142
|
};
|
|
369134
369143
|
var electricBinary = {
|
|
369135
369144
|
name: "electric",
|
|
@@ -369449,16 +369458,35 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
369449
369458
|
}
|
|
369450
369459
|
};
|
|
369451
369460
|
}
|
|
369452
|
-
async function startRedis(redis, port, onProgress) {
|
|
369461
|
+
async function startRedis(redis, port, dataDir, onProgress) {
|
|
369453
369462
|
const binary = await ensureBinary(redisBinary, void 0, onProgress);
|
|
369454
369463
|
const host = "127.0.0.1";
|
|
369464
|
+
const redisDataPath = path5.join(process.cwd(), dataDir, "redis", redis.name);
|
|
369465
|
+
fs5.mkdirSync(redisDataPath, { recursive: true });
|
|
369466
|
+
const stalePath = path5.join(redisDataPath, "dump.rdb");
|
|
369467
|
+
if (fs5.existsSync(stalePath)) {
|
|
369468
|
+
fs5.rmSync(stalePath, { force: true });
|
|
369469
|
+
}
|
|
369455
369470
|
if (await isPortInUse(host, port)) {
|
|
369456
369471
|
await reclaimSpecificOrphanOnPort(port);
|
|
369457
369472
|
await waitForPortFree(host, port, 1e3);
|
|
369458
369473
|
}
|
|
369459
369474
|
const redisProc = spawn(
|
|
369460
|
-
binary.executables["
|
|
369461
|
-
[
|
|
369475
|
+
binary.executables["valkey-server"],
|
|
369476
|
+
[
|
|
369477
|
+
"--port",
|
|
369478
|
+
String(port),
|
|
369479
|
+
"--bind",
|
|
369480
|
+
host,
|
|
369481
|
+
"--daemonize",
|
|
369482
|
+
"no",
|
|
369483
|
+
"--dir",
|
|
369484
|
+
redisDataPath,
|
|
369485
|
+
"--save",
|
|
369486
|
+
"",
|
|
369487
|
+
"--appendonly",
|
|
369488
|
+
"no"
|
|
369489
|
+
],
|
|
369462
369490
|
{
|
|
369463
369491
|
stdio: ["ignore", "pipe", "pipe"],
|
|
369464
369492
|
detached: true
|
|
@@ -370448,6 +370476,417 @@ function killTrackedProcess(pid, detached, isProcessRunning) {
|
|
|
370448
370476
|
} catch {
|
|
370449
370477
|
}
|
|
370450
370478
|
}
|
|
370479
|
+
var MAX_KEY_LENGTH = 1024;
|
|
370480
|
+
var PROXY_URL_EXPIRES_IN = 3600;
|
|
370481
|
+
function validateKey(key) {
|
|
370482
|
+
if (!key || key.length > MAX_KEY_LENGTH) return "Invalid key length";
|
|
370483
|
+
if (key.includes("..")) return "Key must not contain '..'";
|
|
370484
|
+
if (key.includes("\0")) return "Key must not contain null bytes";
|
|
370485
|
+
if (key.startsWith("/")) return "Key must not start with '/'";
|
|
370486
|
+
return null;
|
|
370487
|
+
}
|
|
370488
|
+
var clientCache = /* @__PURE__ */ new Map();
|
|
370489
|
+
function getClient(info) {
|
|
370490
|
+
const cacheKey = `${info.host}:${info.port}:${info.accessKey}`;
|
|
370491
|
+
const cached2 = clientCache.get(cacheKey);
|
|
370492
|
+
if (cached2) return cached2;
|
|
370493
|
+
const client2 = new S3Client({
|
|
370494
|
+
endpoint: `http://${info.host}:${info.port}`,
|
|
370495
|
+
region: "us-east-1",
|
|
370496
|
+
credentials: {
|
|
370497
|
+
accessKeyId: info.accessKey,
|
|
370498
|
+
secretAccessKey: info.secretKey
|
|
370499
|
+
},
|
|
370500
|
+
forcePathStyle: true
|
|
370501
|
+
});
|
|
370502
|
+
clientCache.set(cacheKey, client2);
|
|
370503
|
+
return client2;
|
|
370504
|
+
}
|
|
370505
|
+
function sendJson(res, status, body) {
|
|
370506
|
+
res.setHeader("Content-Type", "application/json");
|
|
370507
|
+
res.writeHead(status);
|
|
370508
|
+
res.end(JSON.stringify(body));
|
|
370509
|
+
}
|
|
370510
|
+
function sendError(res, status, message) {
|
|
370511
|
+
sendJson(res, status, { error: message });
|
|
370512
|
+
}
|
|
370513
|
+
async function readJsonBody(req) {
|
|
370514
|
+
const chunks = [];
|
|
370515
|
+
for await (const chunk of req) {
|
|
370516
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
370517
|
+
}
|
|
370518
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
370519
|
+
return text.length > 0 ? JSON.parse(text) : {};
|
|
370520
|
+
}
|
|
370521
|
+
async function handleListObjects(client2, bucket, url, res) {
|
|
370522
|
+
const prefix = url.searchParams.get("prefix") ?? "";
|
|
370523
|
+
const cursor = url.searchParams.get("cursor") ?? void 0;
|
|
370524
|
+
const maxKeys = Math.min(
|
|
370525
|
+
Math.max(parseInt(url.searchParams.get("maxKeys") ?? "100", 10) || 100, 1),
|
|
370526
|
+
1e3
|
|
370527
|
+
);
|
|
370528
|
+
const response = await client2.send(
|
|
370529
|
+
new ListObjectsV2Command({
|
|
370530
|
+
Bucket: bucket,
|
|
370531
|
+
Prefix: prefix,
|
|
370532
|
+
Delimiter: "/",
|
|
370533
|
+
MaxKeys: maxKeys,
|
|
370534
|
+
ContinuationToken: cursor || void 0
|
|
370535
|
+
})
|
|
370536
|
+
);
|
|
370537
|
+
sendJson(res, 200, {
|
|
370538
|
+
objects: (response.Contents ?? []).map((obj) => ({
|
|
370539
|
+
key: obj.Key,
|
|
370540
|
+
size: obj.Size ?? 0,
|
|
370541
|
+
lastModified: obj.LastModified?.toISOString() ?? "",
|
|
370542
|
+
etag: obj.ETag
|
|
370543
|
+
})),
|
|
370544
|
+
prefixes: (response.CommonPrefixes ?? []).map((p) => p.Prefix),
|
|
370545
|
+
nextCursor: response.NextContinuationToken ?? null,
|
|
370546
|
+
prefix
|
|
370547
|
+
});
|
|
370548
|
+
}
|
|
370549
|
+
async function handleMetrics(client2, bucket, res) {
|
|
370550
|
+
let totalObjects = 0;
|
|
370551
|
+
let totalSize = 0;
|
|
370552
|
+
let continuationToken;
|
|
370553
|
+
let pages = 0;
|
|
370554
|
+
const MAX_PAGES = 100;
|
|
370555
|
+
do {
|
|
370556
|
+
const response = await client2.send(
|
|
370557
|
+
new ListObjectsV2Command({
|
|
370558
|
+
Bucket: bucket,
|
|
370559
|
+
ContinuationToken: continuationToken
|
|
370560
|
+
})
|
|
370561
|
+
);
|
|
370562
|
+
totalObjects += response.KeyCount ?? 0;
|
|
370563
|
+
for (const obj of response.Contents ?? []) {
|
|
370564
|
+
totalSize += obj.Size ?? 0;
|
|
370565
|
+
}
|
|
370566
|
+
continuationToken = response.NextContinuationToken;
|
|
370567
|
+
pages++;
|
|
370568
|
+
} while (continuationToken && pages < MAX_PAGES);
|
|
370569
|
+
sendJson(res, 200, {
|
|
370570
|
+
totalObjects,
|
|
370571
|
+
totalSize,
|
|
370572
|
+
bucketName: bucket,
|
|
370573
|
+
...pages >= MAX_PAGES ? { truncated: true } : {}
|
|
370574
|
+
});
|
|
370575
|
+
}
|
|
370576
|
+
async function handleGetObject(client2, bucket, key, res) {
|
|
370577
|
+
try {
|
|
370578
|
+
const response = await client2.send(
|
|
370579
|
+
new HeadObjectCommand({ Bucket: bucket, Key: key })
|
|
370580
|
+
);
|
|
370581
|
+
sendJson(res, 200, {
|
|
370582
|
+
key,
|
|
370583
|
+
size: response.ContentLength ?? 0,
|
|
370584
|
+
lastModified: response.LastModified?.toISOString(),
|
|
370585
|
+
contentType: response.ContentType,
|
|
370586
|
+
etag: response.ETag,
|
|
370587
|
+
metadata: response.Metadata
|
|
370588
|
+
});
|
|
370589
|
+
} catch (error) {
|
|
370590
|
+
const name = error.name;
|
|
370591
|
+
if (name === "NotFound" || name === "NoSuchKey") {
|
|
370592
|
+
sendError(res, 404, `Object '${key}' not found`);
|
|
370593
|
+
return;
|
|
370594
|
+
}
|
|
370595
|
+
throw error;
|
|
370596
|
+
}
|
|
370597
|
+
}
|
|
370598
|
+
async function handleDeleteObject(client2, bucket, key, res) {
|
|
370599
|
+
if (key.endsWith("/")) {
|
|
370600
|
+
const allKeys = [];
|
|
370601
|
+
let continuationToken;
|
|
370602
|
+
do {
|
|
370603
|
+
const listResponse = await client2.send(
|
|
370604
|
+
new ListObjectsV2Command({
|
|
370605
|
+
Bucket: bucket,
|
|
370606
|
+
Prefix: key,
|
|
370607
|
+
ContinuationToken: continuationToken
|
|
370608
|
+
})
|
|
370609
|
+
);
|
|
370610
|
+
for (const obj of listResponse.Contents ?? []) {
|
|
370611
|
+
if (obj.Key) allKeys.push(obj.Key);
|
|
370612
|
+
}
|
|
370613
|
+
continuationToken = listResponse.NextContinuationToken;
|
|
370614
|
+
} while (continuationToken);
|
|
370615
|
+
const batches = [];
|
|
370616
|
+
for (let i = 0; i < allKeys.length; i += 1e3) {
|
|
370617
|
+
batches.push(
|
|
370618
|
+
client2.send(
|
|
370619
|
+
new DeleteObjectsCommand({
|
|
370620
|
+
Bucket: bucket,
|
|
370621
|
+
Delete: {
|
|
370622
|
+
Objects: allKeys.slice(i, i + 1e3).map((k) => ({ Key: k })),
|
|
370623
|
+
Quiet: true
|
|
370624
|
+
}
|
|
370625
|
+
})
|
|
370626
|
+
)
|
|
370627
|
+
);
|
|
370628
|
+
}
|
|
370629
|
+
await Promise.all(batches);
|
|
370630
|
+
} else {
|
|
370631
|
+
await client2.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
370632
|
+
}
|
|
370633
|
+
sendJson(res, 200, { success: true });
|
|
370634
|
+
}
|
|
370635
|
+
async function handleCreateFolder(client2, bucket, body, res) {
|
|
370636
|
+
const path172 = body.path;
|
|
370637
|
+
if (typeof path172 !== "string") {
|
|
370638
|
+
sendError(res, 400, "Missing path");
|
|
370639
|
+
return;
|
|
370640
|
+
}
|
|
370641
|
+
const folderKey = path172.endsWith("/") ? path172 : `${path172}/`;
|
|
370642
|
+
const keyError = validateKey(folderKey);
|
|
370643
|
+
if (keyError) {
|
|
370644
|
+
sendError(res, 400, keyError);
|
|
370645
|
+
return;
|
|
370646
|
+
}
|
|
370647
|
+
await client2.send(
|
|
370648
|
+
new PutObjectCommand({ Bucket: bucket, Key: folderKey, Body: "" })
|
|
370649
|
+
);
|
|
370650
|
+
sendJson(res, 200, { key: folderKey });
|
|
370651
|
+
}
|
|
370652
|
+
async function handleRename(client2, bucket, body, res) {
|
|
370653
|
+
const { sourceKey, destinationKey } = body;
|
|
370654
|
+
if (typeof sourceKey !== "string" || typeof destinationKey !== "string") {
|
|
370655
|
+
sendError(res, 400, "Missing sourceKey or destinationKey");
|
|
370656
|
+
return;
|
|
370657
|
+
}
|
|
370658
|
+
for (const k of [sourceKey, destinationKey]) {
|
|
370659
|
+
const err = validateKey(k);
|
|
370660
|
+
if (err) {
|
|
370661
|
+
sendError(res, 400, err);
|
|
370662
|
+
return;
|
|
370663
|
+
}
|
|
370664
|
+
}
|
|
370665
|
+
if (sourceKey.endsWith("/")) {
|
|
370666
|
+
const allKeys = [];
|
|
370667
|
+
let continuationToken;
|
|
370668
|
+
do {
|
|
370669
|
+
const listResponse = await client2.send(
|
|
370670
|
+
new ListObjectsV2Command({
|
|
370671
|
+
Bucket: bucket,
|
|
370672
|
+
Prefix: sourceKey,
|
|
370673
|
+
ContinuationToken: continuationToken
|
|
370674
|
+
})
|
|
370675
|
+
);
|
|
370676
|
+
for (const obj of listResponse.Contents ?? []) {
|
|
370677
|
+
if (obj.Key) allKeys.push(obj.Key);
|
|
370678
|
+
}
|
|
370679
|
+
continuationToken = listResponse.NextContinuationToken;
|
|
370680
|
+
} while (continuationToken);
|
|
370681
|
+
if (allKeys.length === 0) {
|
|
370682
|
+
sendError(res, 404, "Folder is empty or does not exist");
|
|
370683
|
+
return;
|
|
370684
|
+
}
|
|
370685
|
+
await Promise.all(
|
|
370686
|
+
allKeys.map(
|
|
370687
|
+
(k) => client2.send(
|
|
370688
|
+
new CopyObjectCommand({
|
|
370689
|
+
Bucket: bucket,
|
|
370690
|
+
CopySource: `${bucket}/${encodeURI(k)}`,
|
|
370691
|
+
Key: destinationKey + k.slice(sourceKey.length)
|
|
370692
|
+
})
|
|
370693
|
+
)
|
|
370694
|
+
)
|
|
370695
|
+
);
|
|
370696
|
+
const deleteBatches = [];
|
|
370697
|
+
for (let i = 0; i < allKeys.length; i += 1e3) {
|
|
370698
|
+
deleteBatches.push(
|
|
370699
|
+
client2.send(
|
|
370700
|
+
new DeleteObjectsCommand({
|
|
370701
|
+
Bucket: bucket,
|
|
370702
|
+
Delete: {
|
|
370703
|
+
Objects: allKeys.slice(i, i + 1e3).map((k) => ({ Key: k })),
|
|
370704
|
+
Quiet: true
|
|
370705
|
+
}
|
|
370706
|
+
})
|
|
370707
|
+
)
|
|
370708
|
+
);
|
|
370709
|
+
}
|
|
370710
|
+
await Promise.all(deleteBatches);
|
|
370711
|
+
} else {
|
|
370712
|
+
await client2.send(
|
|
370713
|
+
new CopyObjectCommand({
|
|
370714
|
+
Bucket: bucket,
|
|
370715
|
+
CopySource: `${bucket}/${encodeURI(sourceKey)}`,
|
|
370716
|
+
Key: destinationKey
|
|
370717
|
+
})
|
|
370718
|
+
);
|
|
370719
|
+
await client2.send(
|
|
370720
|
+
new DeleteObjectCommand({ Bucket: bucket, Key: sourceKey })
|
|
370721
|
+
);
|
|
370722
|
+
}
|
|
370723
|
+
sendJson(res, 200, { key: destinationKey });
|
|
370724
|
+
}
|
|
370725
|
+
async function handleUploadData(client2, bucket, key, req, res) {
|
|
370726
|
+
const chunks = [];
|
|
370727
|
+
for await (const chunk of req) {
|
|
370728
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
370729
|
+
}
|
|
370730
|
+
const body = Buffer.concat(chunks);
|
|
370731
|
+
const contentType = req.headers["content-type"];
|
|
370732
|
+
await client2.send(
|
|
370733
|
+
new PutObjectCommand({
|
|
370734
|
+
Bucket: bucket,
|
|
370735
|
+
Key: key,
|
|
370736
|
+
Body: body,
|
|
370737
|
+
ContentType: typeof contentType === "string" ? contentType : void 0
|
|
370738
|
+
})
|
|
370739
|
+
);
|
|
370740
|
+
res.writeHead(200);
|
|
370741
|
+
res.end();
|
|
370742
|
+
}
|
|
370743
|
+
async function handleDownloadData(client2, bucket, key, res) {
|
|
370744
|
+
try {
|
|
370745
|
+
const response = await client2.send(
|
|
370746
|
+
new GetObjectCommand({ Bucket: bucket, Key: key })
|
|
370747
|
+
);
|
|
370748
|
+
if (response.ContentType) {
|
|
370749
|
+
res.setHeader("Content-Type", response.ContentType);
|
|
370750
|
+
}
|
|
370751
|
+
if (response.ContentLength !== void 0) {
|
|
370752
|
+
res.setHeader("Content-Length", String(response.ContentLength));
|
|
370753
|
+
}
|
|
370754
|
+
res.writeHead(200);
|
|
370755
|
+
const body = response.Body;
|
|
370756
|
+
if (!body) {
|
|
370757
|
+
res.end();
|
|
370758
|
+
return;
|
|
370759
|
+
}
|
|
370760
|
+
if (typeof body.pipe === "function") {
|
|
370761
|
+
body.pipe(res);
|
|
370762
|
+
} else {
|
|
370763
|
+
const stream = body.transformToWebStream();
|
|
370764
|
+
const reader = stream.getReader();
|
|
370765
|
+
while (true) {
|
|
370766
|
+
const { done, value } = await reader.read();
|
|
370767
|
+
if (done) break;
|
|
370768
|
+
if (value) res.write(Buffer.from(value));
|
|
370769
|
+
}
|
|
370770
|
+
res.end();
|
|
370771
|
+
}
|
|
370772
|
+
} catch (error) {
|
|
370773
|
+
const name = error.name;
|
|
370774
|
+
if (name === "NotFound" || name === "NoSuchKey") {
|
|
370775
|
+
if (!res.headersSent) {
|
|
370776
|
+
sendError(res, 404, `Object '${key}' not found`);
|
|
370777
|
+
} else {
|
|
370778
|
+
res.end();
|
|
370779
|
+
}
|
|
370780
|
+
return;
|
|
370781
|
+
}
|
|
370782
|
+
throw error;
|
|
370783
|
+
}
|
|
370784
|
+
}
|
|
370785
|
+
function handlePresigned(storageName, body, res) {
|
|
370786
|
+
const { key, action, expiresIn } = body;
|
|
370787
|
+
if (typeof key !== "string" || action !== "upload" && action !== "download") {
|
|
370788
|
+
sendError(res, 400, "Missing key or invalid action");
|
|
370789
|
+
return;
|
|
370790
|
+
}
|
|
370791
|
+
const keyError = validateKey(key);
|
|
370792
|
+
if (keyError) {
|
|
370793
|
+
sendError(res, 400, keyError);
|
|
370794
|
+
return;
|
|
370795
|
+
}
|
|
370796
|
+
const url = `/api/storage/${encodeURIComponent(storageName)}/data?key=${encodeURIComponent(key)}`;
|
|
370797
|
+
sendJson(res, 200, {
|
|
370798
|
+
url,
|
|
370799
|
+
expiresIn: typeof expiresIn === "number" ? expiresIn : PROXY_URL_EXPIRES_IN
|
|
370800
|
+
});
|
|
370801
|
+
}
|
|
370802
|
+
async function handleStorageRequest(req, res, pathname, url, getStorageInstance) {
|
|
370803
|
+
const match = /^\/api\/storage\/([^/]+)(\/.*)?$/.exec(pathname);
|
|
370804
|
+
if (!match) return false;
|
|
370805
|
+
const storageName = decodeURIComponent(match[1]);
|
|
370806
|
+
const sub = match[2] ?? "";
|
|
370807
|
+
const info = getStorageInstance(storageName);
|
|
370808
|
+
if (!info) {
|
|
370809
|
+
sendError(res, 404, `Storage '${storageName}' not found`);
|
|
370810
|
+
return true;
|
|
370811
|
+
}
|
|
370812
|
+
const client2 = getClient(info);
|
|
370813
|
+
const bucket = info.bucket;
|
|
370814
|
+
try {
|
|
370815
|
+
if (sub === "/objects" && req.method === "GET") {
|
|
370816
|
+
await handleListObjects(client2, bucket, url, res);
|
|
370817
|
+
return true;
|
|
370818
|
+
}
|
|
370819
|
+
if (sub === "/metrics" && req.method === "GET") {
|
|
370820
|
+
await handleMetrics(client2, bucket, res);
|
|
370821
|
+
return true;
|
|
370822
|
+
}
|
|
370823
|
+
if (sub === "/object") {
|
|
370824
|
+
const key = url.searchParams.get("key");
|
|
370825
|
+
if (!key) {
|
|
370826
|
+
sendError(res, 400, "Missing key");
|
|
370827
|
+
return true;
|
|
370828
|
+
}
|
|
370829
|
+
const keyError = validateKey(key);
|
|
370830
|
+
if (keyError) {
|
|
370831
|
+
sendError(res, 400, keyError);
|
|
370832
|
+
return true;
|
|
370833
|
+
}
|
|
370834
|
+
if (req.method === "GET") {
|
|
370835
|
+
await handleGetObject(client2, bucket, key, res);
|
|
370836
|
+
return true;
|
|
370837
|
+
}
|
|
370838
|
+
if (req.method === "DELETE") {
|
|
370839
|
+
await handleDeleteObject(client2, bucket, key, res);
|
|
370840
|
+
return true;
|
|
370841
|
+
}
|
|
370842
|
+
}
|
|
370843
|
+
if (sub === "/data") {
|
|
370844
|
+
const key = url.searchParams.get("key");
|
|
370845
|
+
if (!key) {
|
|
370846
|
+
sendError(res, 400, "Missing key");
|
|
370847
|
+
return true;
|
|
370848
|
+
}
|
|
370849
|
+
const keyError = validateKey(key);
|
|
370850
|
+
if (keyError) {
|
|
370851
|
+
sendError(res, 400, keyError);
|
|
370852
|
+
return true;
|
|
370853
|
+
}
|
|
370854
|
+
if (req.method === "PUT") {
|
|
370855
|
+
await handleUploadData(client2, bucket, key, req, res);
|
|
370856
|
+
return true;
|
|
370857
|
+
}
|
|
370858
|
+
if (req.method === "GET") {
|
|
370859
|
+
await handleDownloadData(client2, bucket, key, res);
|
|
370860
|
+
return true;
|
|
370861
|
+
}
|
|
370862
|
+
}
|
|
370863
|
+
if (sub === "/folder" && req.method === "POST") {
|
|
370864
|
+
const body = await readJsonBody(req);
|
|
370865
|
+
await handleCreateFolder(client2, bucket, body, res);
|
|
370866
|
+
return true;
|
|
370867
|
+
}
|
|
370868
|
+
if (sub === "/rename" && req.method === "POST") {
|
|
370869
|
+
const body = await readJsonBody(req);
|
|
370870
|
+
await handleRename(client2, bucket, body, res);
|
|
370871
|
+
return true;
|
|
370872
|
+
}
|
|
370873
|
+
if (sub === "/presigned" && req.method === "POST") {
|
|
370874
|
+
const body = await readJsonBody(req);
|
|
370875
|
+
handlePresigned(storageName, body, res);
|
|
370876
|
+
return true;
|
|
370877
|
+
}
|
|
370878
|
+
sendError(res, 404, "Not found");
|
|
370879
|
+
return true;
|
|
370880
|
+
} catch (error) {
|
|
370881
|
+
writeLog("admin:storage", `Storage handler error: ${error}`);
|
|
370882
|
+
if (!res.headersSent) {
|
|
370883
|
+
sendError(res, 500, error instanceof Error ? error.message : "Internal error");
|
|
370884
|
+
} else {
|
|
370885
|
+
res.end();
|
|
370886
|
+
}
|
|
370887
|
+
return true;
|
|
370888
|
+
}
|
|
370889
|
+
}
|
|
370451
370890
|
var __dirname = path7.dirname(fileURLToPath(import.meta.url));
|
|
370452
370891
|
var adminDir = path7.join(__dirname, "admin");
|
|
370453
370892
|
var _embeddedAdmin = null;
|
|
@@ -370636,12 +371075,15 @@ function proxyRequest(req, res, targetPort) {
|
|
|
370636
371075
|
});
|
|
370637
371076
|
req.pipe(proxyReq, { end: true });
|
|
370638
371077
|
}
|
|
370639
|
-
async function startAdminServer(getState, listenPort = 0, getLogsDir = () => null) {
|
|
371078
|
+
async function startAdminServer(getState, listenPort = 0, getLogsDir = () => null, getStorageInstance = () => void 0) {
|
|
370640
371079
|
return new Promise((resolve62, reject) => {
|
|
370641
371080
|
const server = http.createServer((req, res) => {
|
|
370642
371081
|
const url = new URL(req.url || "/", "http://localhost");
|
|
370643
371082
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
370644
|
-
res.setHeader(
|
|
371083
|
+
res.setHeader(
|
|
371084
|
+
"Access-Control-Allow-Methods",
|
|
371085
|
+
"GET, POST, PUT, DELETE, OPTIONS"
|
|
371086
|
+
);
|
|
370645
371087
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
370646
371088
|
if (req.method === "OPTIONS") {
|
|
370647
371089
|
res.writeHead(204);
|
|
@@ -370715,6 +371157,10 @@ async function startAdminServer(getState, listenPort = 0, getLogsDir = () => nul
|
|
|
370715
371157
|
res.end(JSON.stringify(body));
|
|
370716
371158
|
return;
|
|
370717
371159
|
}
|
|
371160
|
+
if (pathname.startsWith("/api/storage/")) {
|
|
371161
|
+
void handleStorageRequest(req, res, pathname, url, getStorageInstance);
|
|
371162
|
+
return;
|
|
371163
|
+
}
|
|
370718
371164
|
if (pathname.startsWith("/temporal-ui")) {
|
|
370719
371165
|
const state = getState();
|
|
370720
371166
|
if (state.temporalUiPort) {
|
|
@@ -371713,7 +372159,7 @@ async function startResources(options2) {
|
|
|
371713
372159
|
const port = portAllocator.allocate(`redis:${redis.name}`);
|
|
371714
372160
|
log(`Starting redis "${redis.name}" on port ${port}`);
|
|
371715
372161
|
callbacks.onResourceStarting?.(redis.name, "redis");
|
|
371716
|
-
const instance = await startRedis(redis, port, (progress) => {
|
|
372162
|
+
const instance = await startRedis(redis, port, dataDir, (progress) => {
|
|
371717
372163
|
callbacks.onResourceProgress?.(redis.name, progress);
|
|
371718
372164
|
});
|
|
371719
372165
|
resources.set(redis.name, instance);
|
|
@@ -373342,7 +373788,20 @@ Add them to the config block in specific.local`
|
|
|
373342
373788
|
const adminServer = await startAdminServer(
|
|
373343
373789
|
getState,
|
|
373344
373790
|
adminPort,
|
|
373345
|
-
() => this.serviceLogFiles?.logsDir ?? null
|
|
373791
|
+
() => this.serviceLogFiles?.logsDir ?? null,
|
|
373792
|
+
(name) => {
|
|
373793
|
+
const r = this.resources.get(name);
|
|
373794
|
+
if (!r || r.type !== "storage" || !r.bucket || !r.accessKey || !r.secretKey) {
|
|
373795
|
+
return void 0;
|
|
373796
|
+
}
|
|
373797
|
+
return {
|
|
373798
|
+
host: r.host,
|
|
373799
|
+
port: r.port,
|
|
373800
|
+
bucket: r.bucket,
|
|
373801
|
+
accessKey: r.accessKey,
|
|
373802
|
+
secretKey: r.secretKey
|
|
373803
|
+
};
|
|
373804
|
+
}
|
|
373346
373805
|
);
|
|
373347
373806
|
this.adminServer = adminServer;
|
|
373348
373807
|
this.systemLog(
|
|
@@ -373932,7 +374391,7 @@ function getProjectId() {
|
|
|
373932
374391
|
}
|
|
373933
374392
|
return void 0;
|
|
373934
374393
|
}
|
|
373935
|
-
function
|
|
374394
|
+
function getClient2() {
|
|
373936
374395
|
if (!isEnabled()) return null;
|
|
373937
374396
|
if (!client) {
|
|
373938
374397
|
client = new PostHog("phc_qNQCEUXh6ErdciQRRmeG2xlVvwFjkcW6A5bnOFJ8vXZ", {
|
|
@@ -373945,7 +374404,7 @@ function getClient() {
|
|
|
373945
374404
|
return client;
|
|
373946
374405
|
}
|
|
373947
374406
|
function trackEvent(event, properties) {
|
|
373948
|
-
const ph =
|
|
374407
|
+
const ph = getClient2();
|
|
373949
374408
|
if (!ph) return;
|
|
373950
374409
|
const userId = getUserId();
|
|
373951
374410
|
if (userId && !identified) {
|
|
@@ -373966,7 +374425,7 @@ function trackEvent(event, properties) {
|
|
|
373966
374425
|
event,
|
|
373967
374426
|
properties: {
|
|
373968
374427
|
...properties,
|
|
373969
|
-
cli_version: "0.1.
|
|
374428
|
+
cli_version: "0.1.146",
|
|
373970
374429
|
platform: process.platform,
|
|
373971
374430
|
node_version: process.version,
|
|
373972
374431
|
project_id: getProjectId()
|
|
@@ -376209,6 +376668,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376209
376668
|
setState((s) => ({
|
|
376210
376669
|
...s,
|
|
376211
376670
|
phase: "creating-tarball",
|
|
376671
|
+
environmentId: match.id,
|
|
376212
376672
|
environmentName: match.name,
|
|
376213
376673
|
environments
|
|
376214
376674
|
}));
|
|
@@ -376222,6 +376682,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376222
376682
|
setState((s) => ({
|
|
376223
376683
|
...s,
|
|
376224
376684
|
phase: "creating-tarball",
|
|
376685
|
+
environmentId: match.id,
|
|
376225
376686
|
environmentName: match.name,
|
|
376226
376687
|
environments
|
|
376227
376688
|
}));
|
|
@@ -376233,6 +376694,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376233
376694
|
setState((s) => ({
|
|
376234
376695
|
...s,
|
|
376235
376696
|
phase: "creating-tarball",
|
|
376697
|
+
environmentId: environments[0].id,
|
|
376236
376698
|
environmentName: environments[0].name,
|
|
376237
376699
|
environments
|
|
376238
376700
|
}));
|
|
@@ -376281,6 +376743,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376281
376743
|
setState((s) => ({
|
|
376282
376744
|
...s,
|
|
376283
376745
|
phase: "creating-tarball",
|
|
376746
|
+
environmentId: previewEnv.id,
|
|
376284
376747
|
environmentName: previewEnv.name
|
|
376285
376748
|
}));
|
|
376286
376749
|
} catch (err) {
|
|
@@ -376302,6 +376765,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376302
376765
|
setState((s) => ({
|
|
376303
376766
|
...s,
|
|
376304
376767
|
phase: "creating-tarball",
|
|
376768
|
+
environmentId: env2.id,
|
|
376305
376769
|
environmentName: env2.name
|
|
376306
376770
|
}));
|
|
376307
376771
|
},
|
|
@@ -376453,7 +376917,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376453
376917
|
}, [state]);
|
|
376454
376918
|
const environment = state.environmentName || "prod";
|
|
376455
376919
|
useEffect6(() => {
|
|
376456
|
-
if (state.phase !== "creating-tarball" || !state.projectId || !state.environmentName) return;
|
|
376920
|
+
if (state.phase !== "creating-tarball" || !state.projectId || !state.environmentId || !state.environmentName) return;
|
|
376457
376921
|
let cancelled = false;
|
|
376458
376922
|
async function runDeploy() {
|
|
376459
376923
|
const projectDir = process.cwd();
|
|
@@ -376489,7 +376953,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376489
376953
|
try {
|
|
376490
376954
|
writeLog("deploy", `Creating deployment for project ${state.projectId}`);
|
|
376491
376955
|
const gitInfo = getGitInfo(projectDir);
|
|
376492
|
-
deployment2 = await client2.createDeployment(state.projectId, state.
|
|
376956
|
+
deployment2 = await client2.createDeployment(state.projectId, state.environmentId, {
|
|
376493
376957
|
triggeredBy: "cli",
|
|
376494
376958
|
...gitInfo && {
|
|
376495
376959
|
gitCommitSha: gitInfo.commitSha,
|
|
@@ -376954,7 +377418,7 @@ async function runDeployPipeline(options2) {
|
|
|
376954
377418
|
const projects = await client2.listProjects();
|
|
376955
377419
|
const project = projects.find((p) => p.id === projectId);
|
|
376956
377420
|
const environments = project?.environments ?? [];
|
|
376957
|
-
let
|
|
377421
|
+
let environmentId;
|
|
376958
377422
|
if (options2.environment) {
|
|
376959
377423
|
const match = findEnvironmentByNameOrId(environments, options2.environment);
|
|
376960
377424
|
if (!match) {
|
|
@@ -376964,15 +377428,15 @@ async function runDeployPipeline(options2) {
|
|
|
376964
377428
|
);
|
|
376965
377429
|
process.exit(1);
|
|
376966
377430
|
}
|
|
376967
|
-
|
|
377431
|
+
environmentId = match.id;
|
|
376968
377432
|
writeEnvironmentId(match.id);
|
|
376969
377433
|
} else if (hasEnvironmentId(projectDir)) {
|
|
376970
377434
|
const savedEnvId = readEnvironmentId(projectDir);
|
|
376971
377435
|
const env2 = environments.find((e) => e.id === savedEnvId);
|
|
376972
377436
|
if (env2) {
|
|
376973
|
-
|
|
377437
|
+
environmentId = env2.id;
|
|
376974
377438
|
} else if (environments.length === 1) {
|
|
376975
|
-
|
|
377439
|
+
environmentId = environments[0].id;
|
|
376976
377440
|
writeEnvironmentId(environments[0].id);
|
|
376977
377441
|
} else if (environments.length === 0) {
|
|
376978
377442
|
console.error("Error: No environments found for this project");
|
|
@@ -376987,7 +377451,7 @@ async function runDeployPipeline(options2) {
|
|
|
376987
377451
|
}
|
|
376988
377452
|
} else {
|
|
376989
377453
|
if (environments.length === 1) {
|
|
376990
|
-
|
|
377454
|
+
environmentId = environments[0].id;
|
|
376991
377455
|
writeEnvironmentId(environments[0].id);
|
|
376992
377456
|
} else if (environments.length === 0) {
|
|
376993
377457
|
console.error("Error: No environments found for this project");
|
|
@@ -377034,7 +377498,7 @@ async function runDeployPipeline(options2) {
|
|
|
377034
377498
|
let deployment;
|
|
377035
377499
|
try {
|
|
377036
377500
|
const gitInfo = getGitInfo(projectDir);
|
|
377037
|
-
deployment = await client2.createDeployment(projectId,
|
|
377501
|
+
deployment = await client2.createDeployment(projectId, environmentId, {
|
|
377038
377502
|
triggeredBy: "cli",
|
|
377039
377503
|
...gitInfo && {
|
|
377040
377504
|
gitCommitSha: gitInfo.commitSha,
|
|
@@ -378077,7 +378541,7 @@ function compareVersions(a, b) {
|
|
|
378077
378541
|
return 0;
|
|
378078
378542
|
}
|
|
378079
378543
|
async function checkForUpdate() {
|
|
378080
|
-
const currentVersion = "0.1.
|
|
378544
|
+
const currentVersion = "0.1.146";
|
|
378081
378545
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
378082
378546
|
if (!response.ok) {
|
|
378083
378547
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -378592,7 +379056,7 @@ function friendlyErrorMessage(raw) {
|
|
|
378592
379056
|
var program = new Command();
|
|
378593
379057
|
var env = "production";
|
|
378594
379058
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
378595
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
379059
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.146").enablePositionalOptions();
|
|
378596
379060
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
|
|
378597
379061
|
Examples:
|
|
378598
379062
|
$ specific init
|
package/dist/docs/redis.md
CHANGED