@specific.dev/cli 0.1.225 → 0.1.227
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 +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/crons/__next.!KGRlZmF1bHQp.crons.__PAGE__.txt +1 -1
- package/dist/admin/crons/__next.!KGRlZmF1bHQp.crons.txt +1 -1
- package/dist/admin/crons/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/crons/__next._full.txt +1 -1
- package/dist/admin/crons/__next._head.txt +1 -1
- package/dist/admin/crons/__next._index.txt +1 -1
- package/dist/admin/crons/__next._tree.txt +1 -1
- package/dist/admin/crons/index.html +1 -1
- package/dist/admin/crons/index.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/fullscreen/__next._full.txt +1 -1
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +1 -1
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +1 -1
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/mail/__next._full.txt +1 -1
- package/dist/admin/mail/__next._head.txt +1 -1
- package/dist/admin/mail/__next._index.txt +1 -1
- package/dist/admin/mail/__next._tree.txt +1 -1
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +1 -1
- package/dist/admin/services/__next.!KGRlZmF1bHQp.services.__PAGE__.txt +1 -1
- package/dist/admin/services/__next.!KGRlZmF1bHQp.services.txt +1 -1
- package/dist/admin/services/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/services/__next._full.txt +1 -1
- package/dist/admin/services/__next._head.txt +1 -1
- package/dist/admin/services/__next._index.txt +1 -1
- package/dist/admin/services/__next._tree.txt +1 -1
- package/dist/admin/services/index.html +1 -1
- package/dist/admin/services/index.txt +1 -1
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.storage.__PAGE__.txt +1 -1
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.storage.txt +1 -1
- package/dist/admin/storage/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/storage/__next._full.txt +1 -1
- package/dist/admin/storage/__next._head.txt +1 -1
- package/dist/admin/storage/__next._index.txt +1 -1
- package/dist/admin/storage/__next._tree.txt +1 -1
- package/dist/admin/storage/index.html +1 -1
- package/dist/admin/storage/index.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +1 -1
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +1 -1
- package/dist/admin/workflows/__next._tree.txt +1 -1
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +1 -1
- package/dist/cli.js +242 -38
- package/dist/docs/observability.md +31 -2
- package/dist/docs/postgres.md +1 -0
- package/package.json +1 -1
- /package/dist/admin/_next/static/{qZML8zQZ-3IhVYlWFyLkg → OM2zkE6eRNMAcnrnJnUK2}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{qZML8zQZ-3IhVYlWFyLkg → OM2zkE6eRNMAcnrnJnUK2}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{qZML8zQZ-3IhVYlWFyLkg → OM2zkE6eRNMAcnrnJnUK2}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1118,11 +1118,11 @@ var init_open = __esm({
|
|
|
1118
1118
|
}
|
|
1119
1119
|
const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
|
|
1120
1120
|
if (options2.wait) {
|
|
1121
|
-
return new Promise((resolve10,
|
|
1122
|
-
subprocess.once("error",
|
|
1121
|
+
return new Promise((resolve10, reject2) => {
|
|
1122
|
+
subprocess.once("error", reject2);
|
|
1123
1123
|
subprocess.once("close", (exitCode) => {
|
|
1124
1124
|
if (!options2.allowNonzeroExitCode && exitCode !== 0) {
|
|
1125
|
-
|
|
1125
|
+
reject2(new Error(`Exited with code ${exitCode}`));
|
|
1126
1126
|
return;
|
|
1127
1127
|
}
|
|
1128
1128
|
resolve10(subprocess);
|
|
@@ -1130,13 +1130,13 @@ var init_open = __esm({
|
|
|
1130
1130
|
});
|
|
1131
1131
|
}
|
|
1132
1132
|
if (isFallbackAttempt) {
|
|
1133
|
-
return new Promise((resolve10,
|
|
1134
|
-
subprocess.once("error",
|
|
1133
|
+
return new Promise((resolve10, reject2) => {
|
|
1134
|
+
subprocess.once("error", reject2);
|
|
1135
1135
|
subprocess.once("spawn", () => {
|
|
1136
1136
|
subprocess.once("close", (exitCode) => {
|
|
1137
|
-
subprocess.off("error",
|
|
1137
|
+
subprocess.off("error", reject2);
|
|
1138
1138
|
if (exitCode !== 0) {
|
|
1139
|
-
|
|
1139
|
+
reject2(new Error(`Exited with code ${exitCode}`));
|
|
1140
1140
|
return;
|
|
1141
1141
|
}
|
|
1142
1142
|
subprocess.unref();
|
|
@@ -1146,10 +1146,10 @@ var init_open = __esm({
|
|
|
1146
1146
|
});
|
|
1147
1147
|
}
|
|
1148
1148
|
subprocess.unref();
|
|
1149
|
-
return new Promise((resolve10,
|
|
1150
|
-
subprocess.once("error",
|
|
1149
|
+
return new Promise((resolve10, reject2) => {
|
|
1150
|
+
subprocess.once("error", reject2);
|
|
1151
1151
|
subprocess.once("spawn", () => {
|
|
1152
|
-
subprocess.off("error",
|
|
1152
|
+
subprocess.off("error", reject2);
|
|
1153
1153
|
resolve10(subprocess);
|
|
1154
1154
|
});
|
|
1155
1155
|
});
|
|
@@ -184314,9 +184314,9 @@ var NodeFsHandler = class {
|
|
|
184314
184314
|
this._addToNodeFs(path28, initialAdd, wh, depth + 1);
|
|
184315
184315
|
}
|
|
184316
184316
|
}).on(EV.ERROR, this._boundHandleError);
|
|
184317
|
-
return new Promise((resolve10,
|
|
184317
|
+
return new Promise((resolve10, reject2) => {
|
|
184318
184318
|
if (!stream)
|
|
184319
|
-
return
|
|
184319
|
+
return reject2();
|
|
184320
184320
|
stream.once(STR_END, () => {
|
|
184321
184321
|
if (this.fsw.closed) {
|
|
184322
184322
|
stream = void 0;
|
|
@@ -185187,6 +185187,8 @@ import * as path4 from "path";
|
|
|
185187
185187
|
import * as os2 from "os";
|
|
185188
185188
|
import { createReadStream } from "fs";
|
|
185189
185189
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185190
|
+
import { timingSafeEqual } from "node:crypto";
|
|
185191
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
185190
185192
|
import * as net from "net";
|
|
185191
185193
|
import { execFile } from "child_process";
|
|
185192
185194
|
import { promisify } from "util";
|
|
@@ -185207,7 +185209,7 @@ import {
|
|
|
185207
185209
|
HeadObjectCommand,
|
|
185208
185210
|
ListObjectsV2Command,
|
|
185209
185211
|
PutObjectCommand,
|
|
185210
|
-
S3Client
|
|
185212
|
+
S3Client as S3Client2
|
|
185211
185213
|
} from "@aws-sdk/client-s3";
|
|
185212
185214
|
import * as net3 from "net";
|
|
185213
185215
|
import * as fs8 from "fs";
|
|
@@ -369627,9 +369629,9 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
369627
369629
|
});
|
|
369628
369630
|
}
|
|
369629
369631
|
}
|
|
369630
|
-
await new Promise((resolve62,
|
|
369632
|
+
await new Promise((resolve62, reject2) => {
|
|
369631
369633
|
fileStream.end((err) => {
|
|
369632
|
-
if (err)
|
|
369634
|
+
if (err) reject2(err);
|
|
369633
369635
|
else resolve62();
|
|
369634
369636
|
});
|
|
369635
369637
|
});
|
|
@@ -369883,6 +369885,199 @@ function terminateProcess(proc, opts = {}) {
|
|
|
369883
369885
|
}
|
|
369884
369886
|
});
|
|
369885
369887
|
}
|
|
369888
|
+
function reject(ctx, status, code) {
|
|
369889
|
+
ctx.status = status;
|
|
369890
|
+
ctx.type = "application/xml";
|
|
369891
|
+
ctx.body = `<?xml version="1.0" encoding="UTF-8"?><Error><Code>${code}</Code><Message>${code}</Message></Error>`;
|
|
369892
|
+
}
|
|
369893
|
+
function dateFromAmz(value) {
|
|
369894
|
+
const match = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(value);
|
|
369895
|
+
if (!match) return null;
|
|
369896
|
+
const date = /* @__PURE__ */ new Date(
|
|
369897
|
+
`${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}Z`
|
|
369898
|
+
);
|
|
369899
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
369900
|
+
}
|
|
369901
|
+
function storageGuard(options2) {
|
|
369902
|
+
const client = new S3Client({
|
|
369903
|
+
region: "us-east-1",
|
|
369904
|
+
credentials: {
|
|
369905
|
+
accessKeyId: options2.accessKey,
|
|
369906
|
+
secretAccessKey: options2.secretKey
|
|
369907
|
+
}
|
|
369908
|
+
});
|
|
369909
|
+
const locks = /* @__PURE__ */ new Map();
|
|
369910
|
+
async function authenticated(req, url) {
|
|
369911
|
+
const authorization = req.headers.authorization;
|
|
369912
|
+
const presigned = url.searchParams.has("X-Amz-Algorithm");
|
|
369913
|
+
if (presigned && authorization) return false;
|
|
369914
|
+
let credential;
|
|
369915
|
+
let signedHeaders;
|
|
369916
|
+
let signature;
|
|
369917
|
+
if (presigned) {
|
|
369918
|
+
if (url.searchParams.get("X-Amz-Algorithm") !== "AWS4-HMAC-SHA256")
|
|
369919
|
+
return false;
|
|
369920
|
+
credential = url.searchParams.get("X-Amz-Credential") ?? "";
|
|
369921
|
+
signedHeaders = url.searchParams.get("X-Amz-SignedHeaders") ?? "";
|
|
369922
|
+
signature = url.searchParams.get("X-Amz-Signature") ?? "";
|
|
369923
|
+
} else {
|
|
369924
|
+
const parts = /^AWS4-HMAC-SHA256\s+Credential=([^,]+),\s*SignedHeaders=([^,]+),\s*Signature=([a-fA-F0-9]{64})$/.exec(
|
|
369925
|
+
authorization ?? ""
|
|
369926
|
+
);
|
|
369927
|
+
if (!parts) return false;
|
|
369928
|
+
[, credential = "", signedHeaders = "", signature = ""] = parts;
|
|
369929
|
+
}
|
|
369930
|
+
const [accessKey, day, region, service, end, extra] = credential.split("/");
|
|
369931
|
+
if (accessKey !== options2.accessKey || !region || service !== "s3" || end !== "aws4_request" || extra !== void 0)
|
|
369932
|
+
return false;
|
|
369933
|
+
if (!/^[a-fA-F0-9]{64}$/.test(signature)) return false;
|
|
369934
|
+
const names = signedHeaders.split(";");
|
|
369935
|
+
if (!names.includes("host") || names.join(";") !== [...new Set(names)].sort().join(";"))
|
|
369936
|
+
return false;
|
|
369937
|
+
const timestamp = presigned ? url.searchParams.get("X-Amz-Date") : req.headers["x-amz-date"];
|
|
369938
|
+
if (typeof timestamp !== "string" || day !== timestamp.slice(0, 8))
|
|
369939
|
+
return false;
|
|
369940
|
+
const signingDate = dateFromAmz(timestamp);
|
|
369941
|
+
if (!signingDate) return false;
|
|
369942
|
+
const age = (Date.now() - signingDate.getTime()) / 1e3;
|
|
369943
|
+
const expiresIn = presigned ? Number(url.searchParams.get("X-Amz-Expires")) : 900;
|
|
369944
|
+
if (!Number.isInteger(expiresIn) || expiresIn < 1 || expiresIn > 604800 || age < -900 || age > expiresIn)
|
|
369945
|
+
return false;
|
|
369946
|
+
const headers = {};
|
|
369947
|
+
for (const name of names) {
|
|
369948
|
+
const value = req.headers[name];
|
|
369949
|
+
if (typeof value !== "string" || name !== name.toLowerCase())
|
|
369950
|
+
return false;
|
|
369951
|
+
headers[name] = value;
|
|
369952
|
+
}
|
|
369953
|
+
const payloadHash = req.headers["x-amz-content-sha256"] ?? url.searchParams.get("X-Amz-Content-Sha256") ?? url.searchParams.get("x-amz-content-sha256") ?? (presigned ? "UNSIGNED-PAYLOAD" : void 0);
|
|
369954
|
+
if (typeof payloadHash === "string")
|
|
369955
|
+
headers["x-amz-content-sha256"] = payloadHash;
|
|
369956
|
+
const query = {};
|
|
369957
|
+
for (const [key, value] of url.searchParams) {
|
|
369958
|
+
if (key === "X-Amz-Signature") continue;
|
|
369959
|
+
const old = query[key];
|
|
369960
|
+
query[key] = old === void 0 ? value : [...Array.isArray(old) ? old : [old], value];
|
|
369961
|
+
}
|
|
369962
|
+
const request2 = {
|
|
369963
|
+
method: req.method ?? "GET",
|
|
369964
|
+
protocol: url.protocol,
|
|
369965
|
+
hostname: url.hostname,
|
|
369966
|
+
port: Number(url.port),
|
|
369967
|
+
path: url.pathname,
|
|
369968
|
+
headers,
|
|
369969
|
+
query
|
|
369970
|
+
};
|
|
369971
|
+
const signingOptions = {
|
|
369972
|
+
signingDate,
|
|
369973
|
+
signingRegion: region,
|
|
369974
|
+
signingService: "s3",
|
|
369975
|
+
signableHeaders: new Set(names),
|
|
369976
|
+
unsignableHeaders: new Set(
|
|
369977
|
+
Object.keys(headers).filter((name) => !names.includes(name))
|
|
369978
|
+
)
|
|
369979
|
+
};
|
|
369980
|
+
const signer = await client.config.signer();
|
|
369981
|
+
let expected;
|
|
369982
|
+
if (presigned) {
|
|
369983
|
+
const result = await signer.presign(request2, {
|
|
369984
|
+
...signingOptions,
|
|
369985
|
+
expiresIn,
|
|
369986
|
+
unhoistableHeaders: new Set(Object.keys(headers))
|
|
369987
|
+
});
|
|
369988
|
+
const value = result.query?.["X-Amz-Signature"];
|
|
369989
|
+
expected = typeof value === "string" ? value : void 0;
|
|
369990
|
+
} else {
|
|
369991
|
+
const result = await signer.sign(request2, signingOptions);
|
|
369992
|
+
expected = /Signature=([a-f0-9]{64})$/.exec(
|
|
369993
|
+
result.headers.authorization ?? ""
|
|
369994
|
+
)?.[1];
|
|
369995
|
+
}
|
|
369996
|
+
return Boolean(
|
|
369997
|
+
expected && timingSafeEqual(
|
|
369998
|
+
Buffer.from(signature, "hex"),
|
|
369999
|
+
Buffer.from(expected, "hex")
|
|
370000
|
+
)
|
|
370001
|
+
);
|
|
370002
|
+
}
|
|
370003
|
+
return async (ctx, next) => {
|
|
370004
|
+
const req = ctx.req;
|
|
370005
|
+
if (req.method === "OPTIONS") {
|
|
370006
|
+
await next();
|
|
370007
|
+
return;
|
|
370008
|
+
}
|
|
370009
|
+
let url;
|
|
370010
|
+
let bucket;
|
|
370011
|
+
let key;
|
|
370012
|
+
try {
|
|
370013
|
+
url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
370014
|
+
const parts = url.pathname.slice(1).split("/");
|
|
370015
|
+
bucket = decodeURIComponent(parts.shift() ?? "");
|
|
370016
|
+
key = decodeURIComponent(parts.join("/"));
|
|
370017
|
+
} catch {
|
|
370018
|
+
reject(ctx, 400, "InvalidRequest");
|
|
370019
|
+
return;
|
|
370020
|
+
}
|
|
370021
|
+
const hasAuth = Boolean(req.headers.authorization) || [...url.searchParams.keys()].some(
|
|
370022
|
+
(name) => /^(x-amz-|signature$|awsaccesskeyid$)/i.test(name)
|
|
370023
|
+
);
|
|
370024
|
+
const objectRead = options2.public && bucket === options2.bucket && Boolean(key) && (req.method === "GET" || req.method === "HEAD") && !["acl", "tagging", "uploadId", "uploads", "torrent"].some(
|
|
370025
|
+
(name) => url.searchParams.has(name)
|
|
370026
|
+
);
|
|
370027
|
+
let authorized = false;
|
|
370028
|
+
try {
|
|
370029
|
+
authorized = hasAuth && await authenticated(req, url);
|
|
370030
|
+
} catch {
|
|
370031
|
+
}
|
|
370032
|
+
if (hasAuth && !authorized || !authorized && !objectRead) {
|
|
370033
|
+
reject(ctx, 403, "AccessDenied");
|
|
370034
|
+
return;
|
|
370035
|
+
}
|
|
370036
|
+
const mutation = Boolean(key) && ["PUT", "POST", "DELETE"].includes(req.method ?? "");
|
|
370037
|
+
if (!mutation) {
|
|
370038
|
+
await next();
|
|
370039
|
+
return;
|
|
370040
|
+
}
|
|
370041
|
+
const lockKey = `${bucket}/${key}`;
|
|
370042
|
+
const before = locks.get(lockKey) ?? Promise.resolve();
|
|
370043
|
+
let release;
|
|
370044
|
+
const current = new Promise((resolve62) => {
|
|
370045
|
+
release = resolve62;
|
|
370046
|
+
});
|
|
370047
|
+
locks.set(lockKey, current);
|
|
370048
|
+
await before;
|
|
370049
|
+
try {
|
|
370050
|
+
const finalWrite = req.method === "PUT" && !url.searchParams.has("uploadId") || req.method === "POST" && url.searchParams.has("uploadId");
|
|
370051
|
+
const noneMatch = req.headers["if-none-match"];
|
|
370052
|
+
const match = req.headers["if-match"];
|
|
370053
|
+
if (finalWrite && (noneMatch !== void 0 || match !== void 0)) {
|
|
370054
|
+
if (noneMatch !== void 0 && noneMatch !== "*") {
|
|
370055
|
+
reject(ctx, 400, "InvalidArgument");
|
|
370056
|
+
return;
|
|
370057
|
+
}
|
|
370058
|
+
let metadata;
|
|
370059
|
+
try {
|
|
370060
|
+
metadata = await options2.store.getMetadata(bucket, key);
|
|
370061
|
+
} catch (error) {
|
|
370062
|
+
if (error.code !== "ENOENT") throw error;
|
|
370063
|
+
metadata = null;
|
|
370064
|
+
}
|
|
370065
|
+
if (match !== void 0 && !metadata) {
|
|
370066
|
+
reject(ctx, 404, "NoSuchKey");
|
|
370067
|
+
return;
|
|
370068
|
+
}
|
|
370069
|
+
if (noneMatch === "*" && metadata || match !== void 0 && match !== metadata?.etag) {
|
|
370070
|
+
reject(ctx, 412, "PreconditionFailed");
|
|
370071
|
+
return;
|
|
370072
|
+
}
|
|
370073
|
+
}
|
|
370074
|
+
await next();
|
|
370075
|
+
} finally {
|
|
370076
|
+
release();
|
|
370077
|
+
if (locks.get(lockKey) === current) locks.delete(lockKey);
|
|
370078
|
+
}
|
|
370079
|
+
};
|
|
370080
|
+
}
|
|
369886
370081
|
var execFileAsync = promisify(execFile);
|
|
369887
370082
|
function isPortInUse(host, port) {
|
|
369888
370083
|
return new Promise((resolve62) => {
|
|
@@ -370093,9 +370288,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
370093
370288
|
env2
|
|
370094
370289
|
);
|
|
370095
370290
|
})(),
|
|
370096
|
-
new Promise((_,
|
|
370291
|
+
new Promise((_, reject2) => {
|
|
370097
370292
|
postgres.once("exit", (code, signal) => {
|
|
370098
|
-
|
|
370293
|
+
reject2(
|
|
370099
370294
|
new Error(
|
|
370100
370295
|
`PostgreSQL "${pg.name}" exited during startup (code ${code}, signal ${signal}). Port ${port} may already be in use by another process.`
|
|
370101
370296
|
)
|
|
@@ -370195,9 +370390,18 @@ async function startStorage(storage, port, dataDir) {
|
|
|
370195
370390
|
port,
|
|
370196
370391
|
directory: storageDataPath,
|
|
370197
370392
|
silent: true,
|
|
370198
|
-
allowMismatchedSignatures:
|
|
370393
|
+
allowMismatchedSignatures: false,
|
|
370199
370394
|
configureBuckets: [{ name: storage.name }]
|
|
370200
370395
|
});
|
|
370396
|
+
server.middleware.unshift(
|
|
370397
|
+
storageGuard({
|
|
370398
|
+
bucket: storage.name,
|
|
370399
|
+
public: storage.public ?? false,
|
|
370400
|
+
accessKey,
|
|
370401
|
+
secretKey,
|
|
370402
|
+
store: server.store
|
|
370403
|
+
})
|
|
370404
|
+
);
|
|
370201
370405
|
await server.run();
|
|
370202
370406
|
const endpoint = `http://${host}:${port}`;
|
|
370203
370407
|
return {
|
|
@@ -370224,7 +370428,7 @@ async function startStorage(storage, port, dataDir) {
|
|
|
370224
370428
|
};
|
|
370225
370429
|
}
|
|
370226
370430
|
async function runCommand(command, args, env2) {
|
|
370227
|
-
return new Promise((resolve62,
|
|
370431
|
+
return new Promise((resolve62, reject2) => {
|
|
370228
370432
|
const proc = spawn(command, args, {
|
|
370229
370433
|
stdio: ["ignore", "pipe", "pipe"],
|
|
370230
370434
|
env: env2
|
|
@@ -370237,14 +370441,14 @@ async function runCommand(command, args, env2) {
|
|
|
370237
370441
|
if (code === 0) {
|
|
370238
370442
|
resolve62();
|
|
370239
370443
|
} else {
|
|
370240
|
-
|
|
370444
|
+
reject2(new Error(`Command failed with code ${code}: ${stderr}`));
|
|
370241
370445
|
}
|
|
370242
370446
|
});
|
|
370243
|
-
proc.on("error",
|
|
370447
|
+
proc.on("error", reject2);
|
|
370244
370448
|
});
|
|
370245
370449
|
}
|
|
370246
370450
|
async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
370247
|
-
return new Promise((resolve62,
|
|
370451
|
+
return new Promise((resolve62, reject2) => {
|
|
370248
370452
|
const proc = spawn(
|
|
370249
370453
|
postgresPath,
|
|
370250
370454
|
["--single", "-D", dataDir, "postgres"],
|
|
@@ -370260,7 +370464,7 @@ async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
|
370260
370464
|
proc.on("close", (code) => {
|
|
370261
370465
|
resolve62();
|
|
370262
370466
|
});
|
|
370263
|
-
proc.on("error",
|
|
370467
|
+
proc.on("error", reject2);
|
|
370264
370468
|
proc.stdin?.write(`CREATE DATABASE "${dbName}";
|
|
370265
370469
|
`);
|
|
370266
370470
|
proc.stdin?.end();
|
|
@@ -371190,7 +371394,7 @@ function getClient(info) {
|
|
|
371190
371394
|
const cacheKey = `${info.host}:${info.port}:${info.accessKey}`;
|
|
371191
371395
|
const cached2 = clientCache.get(cacheKey);
|
|
371192
371396
|
if (cached2) return cached2;
|
|
371193
|
-
const client = new
|
|
371397
|
+
const client = new S3Client2({
|
|
371194
371398
|
endpoint: `http://${info.host}:${info.port}`,
|
|
371195
371399
|
region: "us-east-1",
|
|
371196
371400
|
credentials: {
|
|
@@ -371776,7 +371980,7 @@ function proxyRequest(req, res, targetPort) {
|
|
|
371776
371980
|
req.pipe(proxyReq, { end: true });
|
|
371777
371981
|
}
|
|
371778
371982
|
async function startAdminServer(getState, listenPort = 0, getLogsDir = () => null, getStorageInstance = () => void 0) {
|
|
371779
|
-
return new Promise((resolve62,
|
|
371983
|
+
return new Promise((resolve62, reject2) => {
|
|
371780
371984
|
const server = http.createServer((req, res) => {
|
|
371781
371985
|
const url = new URL(req.url || "/", "http://localhost");
|
|
371782
371986
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
@@ -371873,11 +372077,11 @@ async function startAdminServer(getState, listenPort = 0, getLogsDir = () => nul
|
|
|
371873
372077
|
}
|
|
371874
372078
|
serveStaticFile(res, pathname);
|
|
371875
372079
|
});
|
|
371876
|
-
server.on("error",
|
|
372080
|
+
server.on("error", reject2);
|
|
371877
372081
|
server.listen(listenPort, "127.0.0.1", () => {
|
|
371878
372082
|
const addr = server.address();
|
|
371879
372083
|
if (!addr || typeof addr === "string") {
|
|
371880
|
-
|
|
372084
|
+
reject2(new Error("Failed to get server address"));
|
|
371881
372085
|
return;
|
|
371882
372086
|
}
|
|
371883
372087
|
const port = addr.port;
|
|
@@ -372160,13 +372364,13 @@ async function startMailServer(mail, smtpPort, apiPort) {
|
|
|
372160
372364
|
res.writeHead(404);
|
|
372161
372365
|
res.end();
|
|
372162
372366
|
});
|
|
372163
|
-
await new Promise((resolve62,
|
|
372367
|
+
await new Promise((resolve62, reject2) => {
|
|
372164
372368
|
smtpServer.listen(smtpPort, "127.0.0.1", () => resolve62());
|
|
372165
|
-
smtpServer.on("error",
|
|
372369
|
+
smtpServer.on("error", reject2);
|
|
372166
372370
|
});
|
|
372167
|
-
await new Promise((resolve62,
|
|
372371
|
+
await new Promise((resolve62, reject2) => {
|
|
372168
372372
|
httpServer.listen(apiPort, "127.0.0.1", () => resolve62());
|
|
372169
|
-
httpServer.on("error",
|
|
372373
|
+
httpServer.on("error", reject2);
|
|
372170
372374
|
});
|
|
372171
372375
|
const stop = async () => {
|
|
372172
372376
|
httpServer.closeAllConnections();
|
|
@@ -375459,7 +375663,7 @@ async function sendEvents(events) {
|
|
|
375459
375663
|
...event.properties,
|
|
375460
375664
|
$geoip_disable: true,
|
|
375461
375665
|
$lib: "specific-cli",
|
|
375462
|
-
$lib_version: "0.1.
|
|
375666
|
+
$lib_version: "0.1.227"
|
|
375463
375667
|
}
|
|
375464
375668
|
}))
|
|
375465
375669
|
});
|
|
@@ -375509,7 +375713,7 @@ function trackEvent(event, properties) {
|
|
|
375509
375713
|
distinct_id: distinctId,
|
|
375510
375714
|
properties: {
|
|
375511
375715
|
...properties,
|
|
375512
|
-
cli_version: "0.1.
|
|
375716
|
+
cli_version: "0.1.227",
|
|
375513
375717
|
platform: process.platform,
|
|
375514
375718
|
node_version: process.version,
|
|
375515
375719
|
project_id: getProjectId()
|
|
@@ -380571,7 +380775,7 @@ function compareVersions(a, b) {
|
|
|
380571
380775
|
return 0;
|
|
380572
380776
|
}
|
|
380573
380777
|
async function checkForUpdate() {
|
|
380574
|
-
const currentVersion = "0.1.
|
|
380778
|
+
const currentVersion = "0.1.227";
|
|
380575
380779
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
380576
380780
|
if (!response.ok) {
|
|
380577
380781
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -381222,12 +381426,12 @@ async function resolveSql(arg) {
|
|
|
381222
381426
|
return await readStdin();
|
|
381223
381427
|
}
|
|
381224
381428
|
async function readStdin() {
|
|
381225
|
-
return new Promise((resolve10,
|
|
381429
|
+
return new Promise((resolve10, reject2) => {
|
|
381226
381430
|
let data = "";
|
|
381227
381431
|
process.stdin.setEncoding("utf-8");
|
|
381228
381432
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
381229
381433
|
process.stdin.on("end", () => resolve10(data.trim()));
|
|
381230
|
-
process.stdin.on("error",
|
|
381434
|
+
process.stdin.on("error", reject2);
|
|
381231
381435
|
});
|
|
381232
381436
|
}
|
|
381233
381437
|
function resolveOutputFormat2(format) {
|
|
@@ -381628,7 +381832,7 @@ async function feedbackCommand(message) {
|
|
|
381628
381832
|
await client.submitFeedback({
|
|
381629
381833
|
message: trimmed,
|
|
381630
381834
|
projectId,
|
|
381631
|
-
cliVersion: "0.1.
|
|
381835
|
+
cliVersion: "0.1.227"
|
|
381632
381836
|
});
|
|
381633
381837
|
trackEvent("feedback_submitted", { hasProject: !!projectId });
|
|
381634
381838
|
console.log("Thanks! We will take a look at your feedback shortly.");
|
|
@@ -381647,7 +381851,7 @@ async function feedbackCommand(message) {
|
|
|
381647
381851
|
var program = new Command();
|
|
381648
381852
|
var env = "production";
|
|
381649
381853
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
381650
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
381854
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.227").enablePositionalOptions();
|
|
381651
381855
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
|
|
381652
381856
|
Examples:
|
|
381653
381857
|
$ specific init
|
|
@@ -142,11 +142,11 @@ OpenTelemetry spans. Today every row is the edge span the platform's ingress rec
|
|
|
142
142
|
| Key | Notes |
|
|
143
143
|
|---|---|
|
|
144
144
|
| `http.method` | `GET`, `POST`, … |
|
|
145
|
-
| `http.target` | Request path
|
|
145
|
+
| `http.target` | Request path. Newly collected spans omit query strings, fragments, and URL userinfo; older retained rows may still contain them |
|
|
146
146
|
| `http.route` | Path with ID-looking segments replaced by `{id}` (`/api/users/{id}`) and static-asset file names by `{file}.ext` (`/assets/{file}.svg`) — a heuristic, since the platform has no route manifest; group by this for per-endpoint views |
|
|
147
147
|
| `http.status_code` | HTTP status as a string — `toUInt16OrZero(SpanAttributes['http.status_code']) >= 500` |
|
|
148
148
|
| `http.server_name` | Hostname the request arrived on |
|
|
149
|
-
| `http.user_agent`, `http.
|
|
149
|
+
| `http.user_agent`, `http.x_forwarded_for`, `net.peer.ip` | Client details |
|
|
150
150
|
| `nginx.upstream_header_time` | Seconds until your service sent its response headers (time to first byte) — how fast the service answered. Unaffected by streamed responses and slow client downloads, so use this for "is my service slow". A comma-separated list when nginx retried another pod; absent when the request never reached your service (nginx's own 408/499) |
|
|
151
151
|
| `nginx.upstream_response_time` | Seconds until your service finished sending its response. `Duration` additionally runs until the client received the last byte. Same list/absent conventions as above |
|
|
152
152
|
|
|
@@ -154,6 +154,35 @@ Also exposed but rarely queried directly: `EnvironmentId`, `ProjectId`.
|
|
|
154
154
|
|
|
155
155
|
Retention: up to 30 days. Your plan may further restrict how far back queries can read; when that cutoff is in effect, `specific query` prints a one-line note on stderr.
|
|
156
156
|
|
|
157
|
+
### Ingress URL privacy
|
|
158
|
+
|
|
159
|
+
After the updated ingress collector configuration rolls out, newly collected
|
|
160
|
+
analytics and request spans omit all query strings, fragments and leading HTTP(S)
|
|
161
|
+
URL userinfo from URL fields and span names. Standalone `url.query` and
|
|
162
|
+
`url.fragment` attributes are removed. Raw Referer is dropped entirely: neither `http.referer` nor its originating
|
|
163
|
+
header is exported. For dashboard analytics only, nginx derives an ICANN
|
|
164
|
+
registrable domain (for example, `news.ycombinator.com/item?id=123` becomes
|
|
165
|
+
`ycombinator.com`, and `www.google.co.uk` becomes `google.co.uk`). Subdomains,
|
|
166
|
+
paths, query strings, fragments, ports and userinfo are not retained as referral
|
|
167
|
+
attribution. Private hosting suffixes are collapsed too: `tenant.github.io`
|
|
168
|
+
becomes `github.io`. IP addresses, unknown suffixes, credentials in the authority,
|
|
169
|
+
malformed URLs and duplicate headers receive no attribution. This field is not
|
|
170
|
+
exported to request traces. Top Referrers uses only the derived domain, with no
|
|
171
|
+
fallback to historical raw URLs. Self-referrals are excluded at the same domain
|
|
172
|
+
granularity, so other sites sharing a parent domain (including `spcf.app`) are
|
|
173
|
+
grouped and excluded together. Domains themselves may still contain sensitive
|
|
174
|
+
information; this is data minimization, not universal secret detection.
|
|
175
|
+
Paths, HTTP status, method and latency remain available; query-dependent RSC and
|
|
176
|
+
Referer-based ACME exclusions still apply to analytics, not request traces.
|
|
177
|
+
|
|
178
|
+
This is a telemetry-only policy: it does not change URLs or headers forwarded to
|
|
179
|
+
your application. Historical rows and copies are not deleted by this change and
|
|
180
|
+
can still contain unredacted data. This is not universal secret detection:
|
|
181
|
+
credentials in path segments, custom attributes or application logs are outside
|
|
182
|
+
this policy. URL-privacy transformation errors reject the incoming telemetry
|
|
183
|
+
payload, potentially losing otherwise valid spans; they do not reject the
|
|
184
|
+
application's HTTP request.
|
|
185
|
+
|
|
157
186
|
### `observability.events`
|
|
158
187
|
|
|
159
188
|
Kubernetes events about your service pods (probe failures, restarts, out-of-memory kills, scheduling problems), in the OpenTelemetry log-record shape. Retention: up to 30 days.
|
package/dist/docs/postgres.md
CHANGED
|
@@ -48,6 +48,7 @@ The following PostgreSQL extensions are available in both development and produc
|
|
|
48
48
|
- **btree_gist** (`btree_gist`) - GiST operator classes for B-tree types (int, text, timestamp, uuid, ...), enabling exclusion constraints (e.g. preventing overlapping time ranges per resource) and multicolumn GiST indexes that mix scalar and GiST-native types. Enable with `CREATE EXTENSION btree_gist;` in a migration. See https://www.postgresql.org/docs/current/btree-gist.html for full documentation.
|
|
49
49
|
- **unaccent** (`unaccent`) - Text search dictionary that strips accents/diacritics, for accent-insensitive search and matching. Enable with `CREATE EXTENSION unaccent;` in a migration. See https://www.postgresql.org/docs/current/unaccent.html for full documentation.
|
|
50
50
|
- **pgcrypto** (`pgcrypto`) - Cryptographic functions: digests and HMAC (SHA-2 family), password hashing with `crypt()`/`gen_salt()` (including bcrypt), PGP symmetric and public-key encryption, and `gen_random_uuid()`/`gen_random_bytes()`. Enable with `CREATE EXTENSION pgcrypto;` in a migration. See https://www.postgresql.org/docs/current/pgcrypto.html for full documentation.
|
|
51
|
+
- **uuid-ossp** (`uuid-ossp`) - UUID generation functions `uuid_generate_v1()`, `uuid_generate_v3()`, `uuid_generate_v4()`, and `uuid_generate_v5()`, for schemas and migrations written against them (e.g. Supabase or Prisma `dbgenerated("uuid_generate_v4()")`). For new code, the built-in `gen_random_uuid()` needs no extension. Enable with `CREATE EXTENSION "uuid-ossp";` in a migration (the quotes are required). See https://www.postgresql.org/docs/current/uuid-ossp.html for full documentation.
|
|
51
52
|
- **pgRouting** (`pgrouting`) - Geospatial routing and network analysis for transportation networks, logistics, and path-finding. Requires PostGIS. Enable with `CREATE EXTENSION pgrouting CASCADE;` in a migration. See https://docs.pgrouting.org/ for full documentation.
|
|
52
53
|
|
|
53
54
|
## Graph data
|
package/package.json
CHANGED
/package/dist/admin/_next/static/{qZML8zQZ-3IhVYlWFyLkg → OM2zkE6eRNMAcnrnJnUK2}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{qZML8zQZ-3IhVYlWFyLkg → OM2zkE6eRNMAcnrnJnUK2}/_ssgManifest.js
RENAMED
|
File without changes
|