@specific.dev/cli 0.1.158 → 0.1.160
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 +2 -2
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +2 -2
- 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/_next/static/chunks/{68598441248beee0.js → d5bf905a3d3f005b.js} +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/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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +2 -2
- 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 +174 -22
- package/dist/docs/services.md +35 -0
- package/package.json +1 -1
- /package/dist/admin/_next/static/{SdGBMqd60o93Rm-No_m_B → e6-YvpGd9Q3oQIstxRX3d}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{SdGBMqd60o93Rm-No_m_B → e6-YvpGd9Q3oQIstxRX3d}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{SdGBMqd60o93Rm-No_m_B → e6-YvpGd9Q3oQIstxRX3d}/_ssgManifest.js +0 -0
|
@@ -18,7 +18,7 @@ f:"$Sreact.suspense"
|
|
|
18
18
|
:HL["/_next/static/media/797e433ab948586e-s.p.479bea2b.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
19
19
|
:HL["/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
20
20
|
:HL["/_next/static/media/caa3a2e1cccd8315-s.p.3b6cae6d.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
21
|
-
0:{"P":null,"b":"
|
|
21
|
+
0:{"P":null,"b":"e6-YvpGd9Q3oQIstxRX3d","c":["","workflows",""],"q":"","i":false,"f":[[["",{"children":["(default)",{"children":["workflows",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/4ae8172f753ceb22.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/c52b2ebc474a0535.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"inter_b2991b2-module__9mH_6q__variable","suppressHydrationWarning":true,"children":["$","body",null,{"className":"geist_a71539c9-module__T19VSG__variable geist_mono_8d43a2aa-module__8Li5zG__variable antialiased","children":["$","$L2",null,{"attribute":"class","defaultTheme":"system","enableSystem":true,"disableTransitionOnChange":true,"children":["$","$L3",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]}]]}],{"children":[["$","$1","c",{"children":[[["$","script","script-0",{"src":"/_next/static/chunks/c7aaec1998a39e17.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/b44581875c51fcd8.js","async":true,"nonce":"$undefined"}],["$","script","script-2",{"src":"/_next/static/chunks/9835cc4e14c5aee3.js","async":true,"nonce":"$undefined"}],["$","script","script-3",{"src":"/_next/static/chunks/dc5c264e3262a84a.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":[["$","$L7",null,{}],["$","$L8",null,{"className":"overflow-hidden","children":["$","$L9",null,{"children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:style","children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:1:props:style","children":404}],["$","div",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:style","children":["$","h2",null,{"style":"$0:f:0:1:0:props:children:1:props:children:props:children:props:children:props:children:props:notFound:0:1:props:children:props:children:2:props:children:props:style","children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$La",null,{"Component":"$b","serverProvidedParams":{"searchParams":{},"params":{},"promises":["$@c","$@d"]}}],[["$","script","script-0",{"src":"/_next/static/chunks/e3baf0c708c5b9ae.js","async":true,"nonce":"$undefined"}]],["$","$Le",null,{"children":["$","$f",null,{"name":"Next.MetadataOutlet","children":"$@10"}]}]]}],{},null,false,false]},null,false,false]},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L11",null,{"children":"$L12"}],["$","div",null,{"hidden":true,"children":["$","$L13",null,{"children":["$","$f",null,{"name":"Next.Metadata","children":"$L14"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$15",[]],"S":true}
|
|
22
22
|
c:{}
|
|
23
23
|
d:"$0:f:0:1:1:children:1:children:1:children:0:props:children:0:props:serverProvidedParams:params"
|
|
24
24
|
12:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
package/dist/cli.js
CHANGED
|
@@ -367507,6 +367507,20 @@ function closeDebugLog() {
|
|
|
367507
367507
|
logStream = null;
|
|
367508
367508
|
}
|
|
367509
367509
|
}
|
|
367510
|
+
var ApiError = class extends Error {
|
|
367511
|
+
code;
|
|
367512
|
+
httpStatus;
|
|
367513
|
+
apiError;
|
|
367514
|
+
pendingActions;
|
|
367515
|
+
constructor(args) {
|
|
367516
|
+
super(`${args.operation}: ${args.apiError} (${args.code})`);
|
|
367517
|
+
this.name = "ApiError";
|
|
367518
|
+
this.code = args.code;
|
|
367519
|
+
this.httpStatus = args.httpStatus;
|
|
367520
|
+
this.apiError = args.apiError;
|
|
367521
|
+
if (args.pendingActions) this.pendingActions = args.pendingActions;
|
|
367522
|
+
}
|
|
367523
|
+
};
|
|
367510
367524
|
async function readApiError(response, operation, requestDescriptor) {
|
|
367511
367525
|
const rawBody = await response.text().catch(() => "");
|
|
367512
367526
|
writeLog("api:error", `Request was: ${requestDescriptor}`);
|
|
@@ -367519,7 +367533,13 @@ async function readApiError(response, operation, requestDescriptor) {
|
|
|
367519
367533
|
const parsed = JSON.parse(rawBody);
|
|
367520
367534
|
if (parsed && typeof parsed === "object" && typeof parsed.error === "string" && typeof parsed.code === "string") {
|
|
367521
367535
|
const apiErr = parsed;
|
|
367522
|
-
return new
|
|
367536
|
+
return new ApiError({
|
|
367537
|
+
operation,
|
|
367538
|
+
code: apiErr.code,
|
|
367539
|
+
httpStatus: response.status,
|
|
367540
|
+
apiError: apiErr.error,
|
|
367541
|
+
...apiErr.pendingActions && { pendingActions: apiErr.pendingActions }
|
|
367542
|
+
});
|
|
367523
367543
|
}
|
|
367524
367544
|
} catch {
|
|
367525
367545
|
}
|
|
@@ -368382,7 +368402,26 @@ function parseBuildRef(build) {
|
|
|
368382
368402
|
}
|
|
368383
368403
|
return void 0;
|
|
368384
368404
|
}
|
|
368385
|
-
function
|
|
368405
|
+
function parseHealthCheckBlock(serviceName, endpointName, healthCheck) {
|
|
368406
|
+
if (healthCheck === void 0) {
|
|
368407
|
+
return void 0;
|
|
368408
|
+
}
|
|
368409
|
+
const arr = Array.isArray(healthCheck) ? healthCheck : [healthCheck];
|
|
368410
|
+
const obj = arr[0];
|
|
368411
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
368412
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" has an invalid health_check block.`);
|
|
368413
|
+
}
|
|
368414
|
+
const fields = obj;
|
|
368415
|
+
const pathValue = Array.isArray(fields.path) ? fields.path[0] : fields.path;
|
|
368416
|
+
if (typeof pathValue !== "string" || pathValue.trim() === "") {
|
|
368417
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" health_check is missing required "path".`);
|
|
368418
|
+
}
|
|
368419
|
+
if (!pathValue.startsWith("/")) {
|
|
368420
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" health_check "path" must start with "/" (got "${pathValue}").`);
|
|
368421
|
+
}
|
|
368422
|
+
return { path: pathValue };
|
|
368423
|
+
}
|
|
368424
|
+
function parseEndpoints(serviceName, fieldObj) {
|
|
368386
368425
|
const endpoints = [];
|
|
368387
368426
|
const endpointData = fieldObj.endpoint;
|
|
368388
368427
|
if (!endpointData) {
|
|
@@ -368396,9 +368435,11 @@ function parseEndpoints(fieldObj) {
|
|
|
368396
368435
|
if (ep && typeof ep === "object" && !Array.isArray(ep)) {
|
|
368397
368436
|
const epObj = ep;
|
|
368398
368437
|
const publicValue = Array.isArray(epObj.public) ? epObj.public[0] : epObj.public;
|
|
368438
|
+
const healthCheck = parseHealthCheckBlock(serviceName, "default", epObj.health_check);
|
|
368399
368439
|
endpoints.push({
|
|
368400
368440
|
name: "default",
|
|
368401
|
-
public: publicValue === true
|
|
368441
|
+
public: publicValue === true,
|
|
368442
|
+
...healthCheck ? { health_check: healthCheck } : {}
|
|
368402
368443
|
});
|
|
368403
368444
|
}
|
|
368404
368445
|
}
|
|
@@ -368408,9 +368449,11 @@ function parseEndpoints(fieldObj) {
|
|
|
368408
368449
|
if (fields && typeof fields === "object" && !Array.isArray(fields)) {
|
|
368409
368450
|
const fieldObj2 = fields;
|
|
368410
368451
|
const publicValue = Array.isArray(fieldObj2.public) ? fieldObj2.public[0] : fieldObj2.public;
|
|
368452
|
+
const healthCheck = parseHealthCheckBlock(serviceName, name, fieldObj2.health_check);
|
|
368411
368453
|
endpoints.push({
|
|
368412
368454
|
name,
|
|
368413
|
-
public: publicValue === true
|
|
368455
|
+
public: publicValue === true,
|
|
368456
|
+
...healthCheck ? { health_check: healthCheck } : {}
|
|
368414
368457
|
});
|
|
368415
368458
|
} else {
|
|
368416
368459
|
endpoints.push({
|
|
@@ -368472,7 +368515,7 @@ function parseServices(serviceData) {
|
|
|
368472
368515
|
if (fieldObj.root) {
|
|
368473
368516
|
service.root = String(fieldObj.root);
|
|
368474
368517
|
}
|
|
368475
|
-
const endpoints = parseEndpoints(fieldObj);
|
|
368518
|
+
const endpoints = parseEndpoints(name, fieldObj);
|
|
368476
368519
|
const volumes = parseVolumes(fieldObj);
|
|
368477
368520
|
if (volumes.length > 0) {
|
|
368478
368521
|
service.volumes = volumes;
|
|
@@ -373932,6 +373975,12 @@ function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
|
373932
373975
|
}
|
|
373933
373976
|
fs3.writeFileSync(path3.join(specificDir, "project_id"), projectId + "\n");
|
|
373934
373977
|
}
|
|
373978
|
+
function clearProjectId(projectDir = process.cwd()) {
|
|
373979
|
+
const projectIdPath = path3.join(projectDir, PROJECT_ID_FILE);
|
|
373980
|
+
if (fs3.existsSync(projectIdPath)) {
|
|
373981
|
+
fs3.unlinkSync(projectIdPath);
|
|
373982
|
+
}
|
|
373983
|
+
}
|
|
373935
373984
|
function readEnvironmentId(projectDir = process.cwd()) {
|
|
373936
373985
|
const envIdPath = path3.join(projectDir, ENVIRONMENT_ID_FILE);
|
|
373937
373986
|
if (!fs3.existsSync(envIdPath)) {
|
|
@@ -373954,6 +374003,12 @@ function writeEnvironmentId(environmentId, projectDir = process.cwd()) {
|
|
|
373954
374003
|
}
|
|
373955
374004
|
fs3.writeFileSync(path3.join(specificDir, "environment_id"), environmentId + "\n");
|
|
373956
374005
|
}
|
|
374006
|
+
function clearEnvironmentId(projectDir = process.cwd()) {
|
|
374007
|
+
const envIdPath = path3.join(projectDir, ENVIRONMENT_ID_FILE);
|
|
374008
|
+
if (fs3.existsSync(envIdPath)) {
|
|
374009
|
+
fs3.unlinkSync(envIdPath);
|
|
374010
|
+
}
|
|
374011
|
+
}
|
|
373957
374012
|
function readPreviewEnvironmentId(projectDir = process.cwd()) {
|
|
373958
374013
|
const filePath = path3.join(projectDir, PREVIEW_ENVIRONMENT_ID_FILE);
|
|
373959
374014
|
if (!fs3.existsSync(filePath)) {
|
|
@@ -374461,7 +374516,7 @@ function trackEvent(event, properties) {
|
|
|
374461
374516
|
event,
|
|
374462
374517
|
properties: {
|
|
374463
374518
|
...properties,
|
|
374464
|
-
cli_version: "0.1.
|
|
374519
|
+
cli_version: "0.1.160",
|
|
374465
374520
|
platform: process.platform,
|
|
374466
374521
|
node_version: process.version,
|
|
374467
374522
|
project_id: getProjectId()
|
|
@@ -375250,7 +375305,26 @@ function parseBuildRef2(build) {
|
|
|
375250
375305
|
}
|
|
375251
375306
|
return void 0;
|
|
375252
375307
|
}
|
|
375253
|
-
function
|
|
375308
|
+
function parseHealthCheckBlock2(serviceName, endpointName, healthCheck) {
|
|
375309
|
+
if (healthCheck === void 0) {
|
|
375310
|
+
return void 0;
|
|
375311
|
+
}
|
|
375312
|
+
const arr = Array.isArray(healthCheck) ? healthCheck : [healthCheck];
|
|
375313
|
+
const obj = arr[0];
|
|
375314
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
375315
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" has an invalid health_check block.`);
|
|
375316
|
+
}
|
|
375317
|
+
const fields = obj;
|
|
375318
|
+
const pathValue = Array.isArray(fields.path) ? fields.path[0] : fields.path;
|
|
375319
|
+
if (typeof pathValue !== "string" || pathValue.trim() === "") {
|
|
375320
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" health_check is missing required "path".`);
|
|
375321
|
+
}
|
|
375322
|
+
if (!pathValue.startsWith("/")) {
|
|
375323
|
+
throw new Error(`Service "${serviceName}" endpoint "${endpointName}" health_check "path" must start with "/" (got "${pathValue}").`);
|
|
375324
|
+
}
|
|
375325
|
+
return { path: pathValue };
|
|
375326
|
+
}
|
|
375327
|
+
function parseEndpoints2(serviceName, fieldObj) {
|
|
375254
375328
|
const endpoints = [];
|
|
375255
375329
|
const endpointData = fieldObj.endpoint;
|
|
375256
375330
|
if (!endpointData) {
|
|
@@ -375264,9 +375338,11 @@ function parseEndpoints2(fieldObj) {
|
|
|
375264
375338
|
if (ep && typeof ep === "object" && !Array.isArray(ep)) {
|
|
375265
375339
|
const epObj = ep;
|
|
375266
375340
|
const publicValue = Array.isArray(epObj.public) ? epObj.public[0] : epObj.public;
|
|
375341
|
+
const healthCheck = parseHealthCheckBlock2(serviceName, "default", epObj.health_check);
|
|
375267
375342
|
endpoints.push({
|
|
375268
375343
|
name: "default",
|
|
375269
|
-
public: publicValue === true
|
|
375344
|
+
public: publicValue === true,
|
|
375345
|
+
...healthCheck ? { health_check: healthCheck } : {}
|
|
375270
375346
|
});
|
|
375271
375347
|
}
|
|
375272
375348
|
}
|
|
@@ -375276,9 +375352,11 @@ function parseEndpoints2(fieldObj) {
|
|
|
375276
375352
|
if (fields && typeof fields === "object" && !Array.isArray(fields)) {
|
|
375277
375353
|
const fieldObj2 = fields;
|
|
375278
375354
|
const publicValue = Array.isArray(fieldObj2.public) ? fieldObj2.public[0] : fieldObj2.public;
|
|
375355
|
+
const healthCheck = parseHealthCheckBlock2(serviceName, name, fieldObj2.health_check);
|
|
375279
375356
|
endpoints.push({
|
|
375280
375357
|
name,
|
|
375281
|
-
public: publicValue === true
|
|
375358
|
+
public: publicValue === true,
|
|
375359
|
+
...healthCheck ? { health_check: healthCheck } : {}
|
|
375282
375360
|
});
|
|
375283
375361
|
} else {
|
|
375284
375362
|
endpoints.push({
|
|
@@ -375340,7 +375418,7 @@ function parseServices2(serviceData) {
|
|
|
375340
375418
|
if (fieldObj.root) {
|
|
375341
375419
|
service.root = String(fieldObj.root);
|
|
375342
375420
|
}
|
|
375343
|
-
const endpoints = parseEndpoints2(fieldObj);
|
|
375421
|
+
const endpoints = parseEndpoints2(name, fieldObj);
|
|
375344
375422
|
const volumes = parseVolumes2(fieldObj);
|
|
375345
375423
|
if (volumes.length > 0) {
|
|
375346
375424
|
service.volumes = volumes;
|
|
@@ -376485,6 +376563,7 @@ function formatErrorCode(code) {
|
|
|
376485
376563
|
const labels = {
|
|
376486
376564
|
pre_deploy_failed: "Pre-deploy hook failed",
|
|
376487
376565
|
deploy_failed: "Service failed to deploy",
|
|
376566
|
+
health_check_failed: "Health check failed",
|
|
376488
376567
|
post_deploy_failed: "Post-deploy hook failed",
|
|
376489
376568
|
internal_error: "Internal error"
|
|
376490
376569
|
};
|
|
@@ -376611,7 +376690,12 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376611
376690
|
const handleProjectSelect = useCallback3(
|
|
376612
376691
|
(project) => {
|
|
376613
376692
|
if ("type" in project && project.type === "new") {
|
|
376614
|
-
setState((s) => ({
|
|
376693
|
+
setState((s) => ({
|
|
376694
|
+
...s,
|
|
376695
|
+
phase: "entering-name",
|
|
376696
|
+
selectedOrganizationId: project.orgId,
|
|
376697
|
+
error: void 0
|
|
376698
|
+
}));
|
|
376615
376699
|
} else {
|
|
376616
376700
|
const proj = project;
|
|
376617
376701
|
writeProjectId(proj.id);
|
|
@@ -376667,7 +376751,17 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376667
376751
|
const client2 = new SpecificClient({ tokenProvider: getValidAccessToken });
|
|
376668
376752
|
const projects2 = await client2.listProjects();
|
|
376669
376753
|
const project = projects2.find((p) => p.id === state.projectId);
|
|
376670
|
-
|
|
376754
|
+
if (!project) {
|
|
376755
|
+
if (cancelled) return;
|
|
376756
|
+
clearProjectId();
|
|
376757
|
+
clearEnvironmentId();
|
|
376758
|
+
setState({
|
|
376759
|
+
phase: "loading-projects",
|
|
376760
|
+
error: "The linked project no longer exists. Pick another to deploy to."
|
|
376761
|
+
});
|
|
376762
|
+
return;
|
|
376763
|
+
}
|
|
376764
|
+
environments = project.environments;
|
|
376671
376765
|
}
|
|
376672
376766
|
if (cancelled) return;
|
|
376673
376767
|
if (environments.length === 0) {
|
|
@@ -376708,6 +376802,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376708
376802
|
return;
|
|
376709
376803
|
}
|
|
376710
376804
|
const projectDir = process.cwd();
|
|
376805
|
+
let staleEnvironmentNote;
|
|
376711
376806
|
if (hasEnvironmentId(projectDir)) {
|
|
376712
376807
|
const savedEnvId = readEnvironmentId(projectDir);
|
|
376713
376808
|
const match = environments.find((e) => e.id === savedEnvId);
|
|
@@ -376721,6 +376816,8 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376721
376816
|
}));
|
|
376722
376817
|
return;
|
|
376723
376818
|
}
|
|
376819
|
+
clearEnvironmentId(projectDir);
|
|
376820
|
+
staleEnvironmentNote = "The linked environment no longer exists. Pick another.";
|
|
376724
376821
|
}
|
|
376725
376822
|
if (environments.length === 1) {
|
|
376726
376823
|
writeEnvironmentId(environments[0].id);
|
|
@@ -376737,7 +376834,8 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376737
376834
|
...s,
|
|
376738
376835
|
phase: "selecting-environment",
|
|
376739
376836
|
environments,
|
|
376740
|
-
environmentSelectedIndex: 0
|
|
376837
|
+
environmentSelectedIndex: 0,
|
|
376838
|
+
...staleEnvironmentNote && { error: staleEnvironmentNote }
|
|
376741
376839
|
}));
|
|
376742
376840
|
} catch (err) {
|
|
376743
376841
|
if (cancelled) return;
|
|
@@ -376798,7 +376896,8 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376798
376896
|
...s,
|
|
376799
376897
|
phase: "creating-tarball",
|
|
376800
376898
|
environmentId: env2.id,
|
|
376801
|
-
environmentName: env2.name
|
|
376899
|
+
environmentName: env2.name,
|
|
376900
|
+
error: void 0
|
|
376802
376901
|
}));
|
|
376803
376902
|
},
|
|
376804
376903
|
[]
|
|
@@ -376994,6 +377093,28 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
376994
377093
|
});
|
|
376995
377094
|
writeLog("deploy", `Deployment created: ${deployment2.id}`);
|
|
376996
377095
|
} catch (err) {
|
|
377096
|
+
if (err instanceof ApiError) {
|
|
377097
|
+
if (err.code === "PROJECT_DELETED" || err.code === "PROJECT_NOT_FOUND") {
|
|
377098
|
+
writeLog("deploy:error", `Linked project ${err.code} \u2014 re-linking`);
|
|
377099
|
+
clearProjectId();
|
|
377100
|
+
clearEnvironmentId();
|
|
377101
|
+
setState({
|
|
377102
|
+
phase: "loading-projects",
|
|
377103
|
+
error: err.code === "PROJECT_DELETED" ? "The linked project was deleted. Pick another to deploy to." : "The linked project no longer exists. Pick another to deploy to."
|
|
377104
|
+
});
|
|
377105
|
+
return;
|
|
377106
|
+
}
|
|
377107
|
+
if (err.code === "ENVIRONMENT_NOT_FOUND") {
|
|
377108
|
+
writeLog("deploy:error", "Linked environment ENVIRONMENT_NOT_FOUND \u2014 re-prompting");
|
|
377109
|
+
clearEnvironmentId();
|
|
377110
|
+
setState((s) => ({
|
|
377111
|
+
phase: "loading-environments",
|
|
377112
|
+
projectId: s.projectId,
|
|
377113
|
+
error: "The linked environment no longer exists. Pick another."
|
|
377114
|
+
}));
|
|
377115
|
+
return;
|
|
377116
|
+
}
|
|
377117
|
+
}
|
|
376997
377118
|
const errorMsg = `Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`;
|
|
376998
377119
|
writeLog("deploy:error", errorMsg);
|
|
376999
377120
|
setState({
|
|
@@ -377262,12 +377383,12 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
377262
377383
|
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Initiating login...")));
|
|
377263
377384
|
}
|
|
377264
377385
|
if (phase === "loading-projects") {
|
|
377265
|
-
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
|
|
377386
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, state.error && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Note: ", state.error), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects...")));
|
|
377266
377387
|
}
|
|
377267
377388
|
if (phase === "selecting-project" && projects && state.organizations && selectedIndex !== void 0) {
|
|
377268
377389
|
const selectorItems = buildSelectorItems(projects, state.organizations);
|
|
377269
377390
|
const selectableIndices = selectorItems.map((item, index) => item.type !== "header" ? index : -1).filter((i) => i >= 0);
|
|
377270
|
-
return /* @__PURE__ */ React7.createElement(
|
|
377391
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, state.error && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Note: ", state.error), /* @__PURE__ */ React7.createElement(
|
|
377271
377392
|
ProjectSelector,
|
|
377272
377393
|
{
|
|
377273
377394
|
projects,
|
|
@@ -377287,7 +377408,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
377287
377408
|
return { ...s, selectedIndex: selectableIndices[nextPos] ?? idx };
|
|
377288
377409
|
})
|
|
377289
377410
|
}
|
|
377290
|
-
);
|
|
377411
|
+
));
|
|
377291
377412
|
}
|
|
377292
377413
|
if (phase === "entering-name") {
|
|
377293
377414
|
return /* @__PURE__ */ React7.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
|
|
@@ -377299,7 +377420,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
377299
377420
|
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading environments..."));
|
|
377300
377421
|
}
|
|
377301
377422
|
if (phase === "selecting-environment" && state.environments && state.environmentSelectedIndex !== void 0) {
|
|
377302
|
-
return /* @__PURE__ */ React7.createElement(
|
|
377423
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, state.error && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Note: ", state.error), /* @__PURE__ */ React7.createElement(
|
|
377303
377424
|
EnvironmentSelector,
|
|
377304
377425
|
{
|
|
377305
377426
|
environments: state.environments,
|
|
@@ -377317,7 +377438,7 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
377317
377438
|
)
|
|
377318
377439
|
}))
|
|
377319
377440
|
}
|
|
377320
|
-
);
|
|
377441
|
+
));
|
|
377321
377442
|
}
|
|
377322
377443
|
const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
|
|
377323
377444
|
const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
|
|
@@ -377419,9 +377540,11 @@ async function runDeployPipeline(options2) {
|
|
|
377419
377540
|
}
|
|
377420
377541
|
const client2 = new SpecificClient({ tokenProvider: getValidAccessToken });
|
|
377421
377542
|
let projectId = options2.projectId;
|
|
377543
|
+
let projectIdFromLinkedFile = false;
|
|
377422
377544
|
if (!projectId) {
|
|
377423
377545
|
if (hasProjectId(projectDir)) {
|
|
377424
377546
|
projectId = readProjectId(projectDir);
|
|
377547
|
+
projectIdFromLinkedFile = true;
|
|
377425
377548
|
} else {
|
|
377426
377549
|
console.error(
|
|
377427
377550
|
"Error: No project linked.\n Create one first: specific project new <name> [--org <org-id>]\n Or pass one directly: specific deploy --project <project-id>"
|
|
@@ -377448,7 +377571,20 @@ async function runDeployPipeline(options2) {
|
|
|
377448
377571
|
}
|
|
377449
377572
|
const projects = await client2.listProjects();
|
|
377450
377573
|
const project = projects.find((p) => p.id === projectId);
|
|
377451
|
-
|
|
377574
|
+
if (!project) {
|
|
377575
|
+
if (projectIdFromLinkedFile) {
|
|
377576
|
+
console.error(
|
|
377577
|
+
"Error: The linked project no longer exists or was deleted.\n Run `specific deploy` interactively to re-link, or remove `.specific/project_id` and try again."
|
|
377578
|
+
);
|
|
377579
|
+
} else {
|
|
377580
|
+
console.error(
|
|
377581
|
+
`Error: Project "${projectId}" not found or not accessible.
|
|
377582
|
+
Check the --project value, or list available projects: specific project list`
|
|
377583
|
+
);
|
|
377584
|
+
}
|
|
377585
|
+
process.exit(1);
|
|
377586
|
+
}
|
|
377587
|
+
const environments = project.environments;
|
|
377452
377588
|
let environmentId;
|
|
377453
377589
|
if (options2.environment) {
|
|
377454
377590
|
const match = findEnvironmentByNameOrId(environments, options2.environment);
|
|
@@ -377537,6 +377673,22 @@ async function runDeployPipeline(options2) {
|
|
|
377537
377673
|
}
|
|
377538
377674
|
});
|
|
377539
377675
|
} catch (err) {
|
|
377676
|
+
if (err instanceof ApiError) {
|
|
377677
|
+
if (err.code === "PROJECT_DELETED" || err.code === "PROJECT_NOT_FOUND") {
|
|
377678
|
+
const what = err.code === "PROJECT_DELETED" ? "The linked project was deleted." : "The linked project no longer exists.";
|
|
377679
|
+
console.error(
|
|
377680
|
+
`Error: ${what}
|
|
377681
|
+
Run \`specific deploy\` interactively to re-link, or remove \`.specific/project_id\` and try again.`
|
|
377682
|
+
);
|
|
377683
|
+
process.exit(1);
|
|
377684
|
+
}
|
|
377685
|
+
if (err.code === "ENVIRONMENT_NOT_FOUND") {
|
|
377686
|
+
console.error(
|
|
377687
|
+
"Error: The linked environment no longer exists.\n Run `specific deploy` interactively to re-link, or remove `.specific/environment_id` and try again."
|
|
377688
|
+
);
|
|
377689
|
+
process.exit(1);
|
|
377690
|
+
}
|
|
377691
|
+
}
|
|
377540
377692
|
console.error(`Error: Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
377541
377693
|
process.exit(1);
|
|
377542
377694
|
}
|
|
@@ -378572,7 +378724,7 @@ function compareVersions(a, b) {
|
|
|
378572
378724
|
return 0;
|
|
378573
378725
|
}
|
|
378574
378726
|
async function checkForUpdate() {
|
|
378575
|
-
const currentVersion = "0.1.
|
|
378727
|
+
const currentVersion = "0.1.160";
|
|
378576
378728
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
378577
378729
|
if (!response.ok) {
|
|
378578
378730
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -379161,7 +379313,7 @@ async function domainAddCommand(service, domain, options2) {
|
|
|
379161
379313
|
var program = new Command();
|
|
379162
379314
|
var env = "production";
|
|
379163
379315
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
379164
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
379316
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.160").enablePositionalOptions();
|
|
379165
379317
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
|
|
379166
379318
|
Examples:
|
|
379167
379319
|
$ specific init
|
package/dist/docs/services.md
CHANGED
|
@@ -121,6 +121,41 @@ service "worker" {
|
|
|
121
121
|
}
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Health checks
|
|
125
|
+
|
|
126
|
+
Add a `health_check` block inside an `endpoint` to tell Specific how to verify the service is healthy. The platform sends an HTTP `GET` to the given path on the endpoint's port — if the response is `2xx`, the service is considered healthy.
|
|
127
|
+
|
|
128
|
+
```hcl
|
|
129
|
+
service "api" {
|
|
130
|
+
build = build.api
|
|
131
|
+
command = "./api"
|
|
132
|
+
|
|
133
|
+
endpoint {
|
|
134
|
+
public = true
|
|
135
|
+
health_check {
|
|
136
|
+
path = "/healthz"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
env = {
|
|
141
|
+
PORT = port
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `path` - The HTTP path to probe (required, must start with `/`). The service must respond with a `2xx` status code when healthy.
|
|
147
|
+
- A health check should be a low-cost endpoint: don't query the database, call other services, or do real work. Just confirm the process is up and able to respond.
|
|
148
|
+
- Only one `health_check` per service is used. If multiple endpoints declare one, the first one wins.
|
|
149
|
+
- Background workers (services without endpoints) don't support `health_check` yet — only HTTP-serving endpoints do.
|
|
150
|
+
|
|
151
|
+
**Behavior in production:**
|
|
152
|
+
|
|
153
|
+
- During a deploy, the new pod doesn't receive traffic until its health check passes. The old version stays up until then, so users never hit a half-started process.
|
|
154
|
+
- If the health check starts failing after the deploy is live, the service is restarted automatically. Traffic is drained from the unhealthy instance well before the restart happens.
|
|
155
|
+
- If the health check never passes during a deploy (typically a misconfigured `path`), the deploy fails fast with a `health_check_failed` error pointing at the probe URL, rather than hanging until the deploy timeout.
|
|
156
|
+
|
|
157
|
+
A handler that returns `200 OK` with a constant body is sufficient. If the service already exposes a root path that always returns `2xx` (e.g. a static site), you can point `health_check` at `/` instead of adding a dedicated route.
|
|
158
|
+
|
|
124
159
|
## Private inter-service communication
|
|
125
160
|
|
|
126
161
|
Services can reference other services' endpoints using `private_url` to communicate with each other over a private network:
|
package/package.json
CHANGED
/package/dist/admin/_next/static/{SdGBMqd60o93Rm-No_m_B → e6-YvpGd9Q3oQIstxRX3d}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{SdGBMqd60o93Rm-No_m_B → e6-YvpGd9Q3oQIstxRX3d}/_ssgManifest.js
RENAMED
|
File without changes
|