@specific.dev/cli 0.1.138 → 0.1.140
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/{9c4a1f968df31582.js → 2dac1d24dcd83b5f.js} +2 -2
- 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/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 +101 -42
- package/package.json +1 -1
- /package/dist/admin/_next/static/{pHCjR5T3AY4B3i9-nU0ND → dIbCgE-_TKsqFpoght8OM}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{pHCjR5T3AY4B3i9-nU0ND → dIbCgE-_TKsqFpoght8OM}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{pHCjR5T3AY4B3i9-nU0ND → dIbCgE-_TKsqFpoght8OM}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -373282,6 +373282,11 @@ function writePreviewEnvironmentId(environmentId, projectDir = process.cwd()) {
|
|
|
373282
373282
|
fs3.writeFileSync(path3.join(specificDir, "preview_environment_id"), environmentId + "\n");
|
|
373283
373283
|
}
|
|
373284
373284
|
|
|
373285
|
+
// src/lib/project/environment.ts
|
|
373286
|
+
function findEnvironmentByNameOrId(environments, flag) {
|
|
373287
|
+
return environments.find((e) => e.id === flag) ?? environments.find((e) => e.name === flag);
|
|
373288
|
+
}
|
|
373289
|
+
|
|
373285
373290
|
// src/lib/auth/credentials.ts
|
|
373286
373291
|
import * as fs19 from "fs";
|
|
373287
373292
|
import * as path17 from "path";
|
|
@@ -373563,6 +373568,50 @@ async function performDeviceCodeLogin(deviceCode) {
|
|
|
373563
373568
|
}
|
|
373564
373569
|
}
|
|
373565
373570
|
|
|
373571
|
+
// src/lib/auth/github-oidc.ts
|
|
373572
|
+
var AUDIENCE = "specific.dev";
|
|
373573
|
+
var REFRESH_BUFFER_MS = 60 * 1e3;
|
|
373574
|
+
var cached = null;
|
|
373575
|
+
function isGithubActions() {
|
|
373576
|
+
return process.env.GITHUB_ACTIONS === "true";
|
|
373577
|
+
}
|
|
373578
|
+
async function fetchGithubActionsIdToken() {
|
|
373579
|
+
const now = Date.now();
|
|
373580
|
+
if (cached && cached.expiresAt - REFRESH_BUFFER_MS > now) {
|
|
373581
|
+
return cached.token;
|
|
373582
|
+
}
|
|
373583
|
+
const url = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
|
373584
|
+
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
|
373585
|
+
if (!url || !requestToken) {
|
|
373586
|
+
throw new Error(
|
|
373587
|
+
"GitHub Actions OIDC token is not available. Add 'permissions: id-token: write' to your workflow job."
|
|
373588
|
+
);
|
|
373589
|
+
}
|
|
373590
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
373591
|
+
const requestUrl = `${url}${separator}audience=${encodeURIComponent(AUDIENCE)}`;
|
|
373592
|
+
const response = await fetch(requestUrl, {
|
|
373593
|
+
method: "GET",
|
|
373594
|
+
headers: {
|
|
373595
|
+
Authorization: `bearer ${requestToken}`,
|
|
373596
|
+
Accept: "application/json; api-version=2.0"
|
|
373597
|
+
}
|
|
373598
|
+
});
|
|
373599
|
+
if (!response.ok) {
|
|
373600
|
+
const body = await response.text().catch(() => "");
|
|
373601
|
+
throw new Error(
|
|
373602
|
+
`Failed to fetch GitHub Actions OIDC token: HTTP ${response.status} ${response.statusText}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
|
|
373603
|
+
);
|
|
373604
|
+
}
|
|
373605
|
+
const json = await response.json();
|
|
373606
|
+
if (typeof json.value !== "string" || json.value.length === 0) {
|
|
373607
|
+
throw new Error("GitHub Actions OIDC token response did not include a 'value' field");
|
|
373608
|
+
}
|
|
373609
|
+
const token = json.value;
|
|
373610
|
+
const expiresAt = getTokenExpiresAt(token) ?? Date.now() + 5 * 60 * 1e3;
|
|
373611
|
+
cached = { token, expiresAt };
|
|
373612
|
+
return token;
|
|
373613
|
+
}
|
|
373614
|
+
|
|
373566
373615
|
// src/lib/auth/credentials.ts
|
|
373567
373616
|
function getUserCredentialsDir() {
|
|
373568
373617
|
return path17.join(os4.homedir(), ".specific");
|
|
@@ -373617,6 +373666,9 @@ async function saveCredentialsFromToken(token) {
|
|
|
373617
373666
|
});
|
|
373618
373667
|
}
|
|
373619
373668
|
async function getValidAccessToken() {
|
|
373669
|
+
if (isGithubActions()) {
|
|
373670
|
+
return await fetchGithubActionsIdToken();
|
|
373671
|
+
}
|
|
373620
373672
|
const credentials = readUserCredentials();
|
|
373621
373673
|
if (!credentials) {
|
|
373622
373674
|
throw new Error("Not logged in. Run 'specific login' first.");
|
|
@@ -373721,7 +373773,7 @@ function trackEvent(event, properties) {
|
|
|
373721
373773
|
event,
|
|
373722
373774
|
properties: {
|
|
373723
373775
|
...properties,
|
|
373724
|
-
cli_version: "0.1.
|
|
373776
|
+
cli_version: "0.1.140",
|
|
373725
373777
|
platform: process.platform,
|
|
373726
373778
|
node_version: process.version,
|
|
373727
373779
|
project_id: getProjectId()
|
|
@@ -375951,12 +376003,12 @@ function DeployUI({ envFlag, preview, config }) {
|
|
|
375951
376003
|
return;
|
|
375952
376004
|
}
|
|
375953
376005
|
if (envFlag) {
|
|
375954
|
-
const match = environments
|
|
376006
|
+
const match = findEnvironmentByNameOrId(environments, envFlag);
|
|
375955
376007
|
if (!match) {
|
|
375956
376008
|
setState((s) => ({
|
|
375957
376009
|
...s,
|
|
375958
376010
|
phase: "error",
|
|
375959
|
-
error: `Environment "${envFlag}" not found. Available: ${environments.map((e) => e.name).join(", ")}`
|
|
376011
|
+
error: `Environment "${envFlag}" not found (matched against name and ID). Available: ${environments.map((e) => e.name).join(", ")}`
|
|
375960
376012
|
}));
|
|
375961
376013
|
return;
|
|
375962
376014
|
}
|
|
@@ -376672,8 +376724,9 @@ async function runDeployPipeline(options2) {
|
|
|
376672
376724
|
let token;
|
|
376673
376725
|
try {
|
|
376674
376726
|
token = await getValidAccessToken();
|
|
376675
|
-
} catch {
|
|
376676
|
-
|
|
376727
|
+
} catch (err) {
|
|
376728
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
376729
|
+
console.error(`Error: ${message}`);
|
|
376677
376730
|
process.exit(1);
|
|
376678
376731
|
}
|
|
376679
376732
|
const client2 = new SpecificClient({ accessToken: token });
|
|
@@ -376703,39 +376756,43 @@ async function runDeployPipeline(options2) {
|
|
|
376703
376756
|
const preview = await client2.createPreviewEnvironment(projectId);
|
|
376704
376757
|
writePreviewEnvironmentId(preview.id, projectDir);
|
|
376705
376758
|
console.log(`Preview environment "${preview.name}" created (expires: ${new Date(preview.expiresAt).toLocaleString()})`);
|
|
376706
|
-
options2.
|
|
376759
|
+
options2.environment = preview.name;
|
|
376707
376760
|
}
|
|
376761
|
+
const projects = await client2.listProjects();
|
|
376762
|
+
const project = projects.find((p) => p.id === projectId);
|
|
376763
|
+
const environments = project?.environments ?? [];
|
|
376708
376764
|
let environmentName;
|
|
376709
|
-
if (options2.
|
|
376710
|
-
|
|
376765
|
+
if (options2.environment) {
|
|
376766
|
+
const match = findEnvironmentByNameOrId(environments, options2.environment);
|
|
376767
|
+
if (!match) {
|
|
376768
|
+
console.error(
|
|
376769
|
+
`Error: Environment "${options2.environment}" not found (matched against name and ID).
|
|
376770
|
+
Available: ${environments.map((e) => e.name).join(", ") || "(none)"}`
|
|
376771
|
+
);
|
|
376772
|
+
process.exit(1);
|
|
376773
|
+
}
|
|
376774
|
+
environmentName = match.name;
|
|
376775
|
+
writeEnvironmentId(match.id);
|
|
376711
376776
|
} else if (hasEnvironmentId(projectDir)) {
|
|
376712
376777
|
const savedEnvId = readEnvironmentId(projectDir);
|
|
376713
|
-
const
|
|
376714
|
-
const project = projects.find((p) => p.id === projectId);
|
|
376715
|
-
const env2 = project?.environments.find((e) => e.id === savedEnvId);
|
|
376778
|
+
const env2 = environments.find((e) => e.id === savedEnvId);
|
|
376716
376779
|
if (env2) {
|
|
376717
376780
|
environmentName = env2.name;
|
|
376781
|
+
} else if (environments.length === 1) {
|
|
376782
|
+
environmentName = environments[0].name;
|
|
376783
|
+
writeEnvironmentId(environments[0].id);
|
|
376784
|
+
} else if (environments.length === 0) {
|
|
376785
|
+
console.error("Error: No environments found for this project");
|
|
376786
|
+
process.exit(1);
|
|
376718
376787
|
} else {
|
|
376719
|
-
|
|
376720
|
-
|
|
376721
|
-
environmentName = environments[0].name;
|
|
376722
|
-
writeEnvironmentId(environments[0].id);
|
|
376723
|
-
} else if (environments.length === 0) {
|
|
376724
|
-
console.error("Error: No environments found for this project");
|
|
376725
|
-
process.exit(1);
|
|
376726
|
-
} else {
|
|
376727
|
-
console.error(
|
|
376728
|
-
`Error: Multiple environments available. Specify one with --env.
|
|
376788
|
+
console.error(
|
|
376789
|
+
`Error: Multiple environments available. Specify one with --environment.
|
|
376729
376790
|
Available: ${environments.map((e) => e.name).join(", ")}
|
|
376730
|
-
Example: specific deploy --
|
|
376731
|
-
|
|
376732
|
-
|
|
376733
|
-
}
|
|
376791
|
+
Example: specific deploy --environment ${environments[0].name}`
|
|
376792
|
+
);
|
|
376793
|
+
process.exit(1);
|
|
376734
376794
|
}
|
|
376735
376795
|
} else {
|
|
376736
|
-
const projects = await client2.listProjects();
|
|
376737
|
-
const project = projects.find((p) => p.id === projectId);
|
|
376738
|
-
const environments = project?.environments ?? [];
|
|
376739
376796
|
if (environments.length === 1) {
|
|
376740
376797
|
environmentName = environments[0].name;
|
|
376741
376798
|
writeEnvironmentId(environments[0].id);
|
|
@@ -376744,9 +376801,9 @@ async function runDeployPipeline(options2) {
|
|
|
376744
376801
|
process.exit(1);
|
|
376745
376802
|
} else {
|
|
376746
376803
|
console.error(
|
|
376747
|
-
`Error: Multiple environments available. Specify one with --
|
|
376804
|
+
`Error: Multiple environments available. Specify one with --environment.
|
|
376748
376805
|
Available: ${environments.map((e) => e.name).join(", ")}
|
|
376749
|
-
Example: specific deploy --
|
|
376806
|
+
Example: specific deploy --environment ${environments[0].name}`
|
|
376750
376807
|
);
|
|
376751
376808
|
process.exit(1);
|
|
376752
376809
|
}
|
|
@@ -376931,7 +376988,7 @@ async function deployCommand(options2) {
|
|
|
376931
376988
|
await runDeployPipeline({
|
|
376932
376989
|
config,
|
|
376933
376990
|
projectId: options2.project,
|
|
376934
|
-
|
|
376991
|
+
environment: options2.environment,
|
|
376935
376992
|
secrets: options2.secret,
|
|
376936
376993
|
configs: options2.config,
|
|
376937
376994
|
preview: options2.preview
|
|
@@ -376942,7 +376999,7 @@ async function deployCommand(options2) {
|
|
|
376942
376999
|
/* @__PURE__ */ React7.createElement(
|
|
376943
377000
|
DeployUI,
|
|
376944
377001
|
{
|
|
376945
|
-
envFlag: options2.
|
|
377002
|
+
envFlag: options2.environment,
|
|
376946
377003
|
preview: options2.preview,
|
|
376947
377004
|
config
|
|
376948
377005
|
}
|
|
@@ -377827,7 +377884,7 @@ function compareVersions(a, b) {
|
|
|
377827
377884
|
return 0;
|
|
377828
377885
|
}
|
|
377829
377886
|
async function checkForUpdate() {
|
|
377830
|
-
const currentVersion = "0.1.
|
|
377887
|
+
const currentVersion = "0.1.140";
|
|
377831
377888
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
377832
377889
|
if (!response.ok) {
|
|
377833
377890
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -378276,7 +378333,7 @@ async function queryCommand(sqlArg, options2) {
|
|
|
378276
378333
|
const projectId = readProjectId();
|
|
378277
378334
|
const token = await getValidAccessToken();
|
|
378278
378335
|
const client2 = new SpecificClient({ accessToken: token });
|
|
378279
|
-
const environmentId = await resolveEnvironment(client2, projectId, options2.
|
|
378336
|
+
const environmentId = await resolveEnvironment(client2, projectId, options2.environment);
|
|
378280
378337
|
const result = await client2.runObservabilityQuery(environmentId, sql);
|
|
378281
378338
|
process.stdout.write(renderTable(result));
|
|
378282
378339
|
} catch (err) {
|
|
@@ -378306,11 +378363,11 @@ async function resolveEnvironment(client2, projectId, envFlag) {
|
|
|
378306
378363
|
const status = await client2.getProjectStatus(projectId);
|
|
378307
378364
|
const environments = status.environments;
|
|
378308
378365
|
if (envFlag) {
|
|
378309
|
-
const match = environments
|
|
378366
|
+
const match = findEnvironmentByNameOrId(environments, envFlag);
|
|
378310
378367
|
if (!match) {
|
|
378311
378368
|
const available = environments.map((e) => e.name).join(", ") || "(none)";
|
|
378312
378369
|
throw new Error(
|
|
378313
|
-
`Environment "${envFlag}" not found. Available: ${available}`
|
|
378370
|
+
`Environment "${envFlag}" not found (matched against name and ID). Available: ${available}`
|
|
378314
378371
|
);
|
|
378315
378372
|
}
|
|
378316
378373
|
return match.id;
|
|
@@ -378329,7 +378386,7 @@ async function resolveEnvironment(client2, projectId, envFlag) {
|
|
|
378329
378386
|
}
|
|
378330
378387
|
const names = environments.map((e) => e.name).join(", ");
|
|
378331
378388
|
throw new Error(
|
|
378332
|
-
`Project has multiple environments (${names}). Pass --
|
|
378389
|
+
`Project has multiple environments (${names}). Pass --environment <name|id>.`
|
|
378333
378390
|
);
|
|
378334
378391
|
}
|
|
378335
378392
|
function friendlyErrorMessage(raw) {
|
|
@@ -378342,7 +378399,7 @@ function friendlyErrorMessage(raw) {
|
|
|
378342
378399
|
var program = new Command();
|
|
378343
378400
|
var env = "production";
|
|
378344
378401
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
378345
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
378402
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.140").enablePositionalOptions();
|
|
378346
378403
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
|
|
378347
378404
|
Examples:
|
|
378348
378405
|
$ specific init
|
|
@@ -378363,10 +378420,11 @@ Examples:
|
|
|
378363
378420
|
const key = options2.key ?? getDefaultKey();
|
|
378364
378421
|
devCommand(key, options2.tunnel ?? false);
|
|
378365
378422
|
});
|
|
378366
|
-
program.command("deploy").description("Deploy to Specific infrastructure").option("--project <id>", "Project ID to deploy to (overrides .projectid file)").option("--
|
|
378423
|
+
program.command("deploy").description("Deploy to Specific infrastructure").option("--project <id>", "Project ID to deploy to (overrides .projectid file)").option("-e, --environment <name|id>", "Target environment (auto-selected if only one exists)").option("--secret <key=value...>", "Secret values (repeatable)").option("--config <key=value...>", "Config values (repeatable)").option("--preview", "Deploy to an ephemeral preview environment").addHelpText("after", `
|
|
378367
378424
|
Examples:
|
|
378368
378425
|
$ specific deploy
|
|
378369
|
-
$ specific deploy --
|
|
378426
|
+
$ specific deploy --environment staging
|
|
378427
|
+
$ specific deploy -e env_0abc123
|
|
378370
378428
|
$ specific deploy --project proj_123
|
|
378371
378429
|
$ specific deploy --preview
|
|
378372
378430
|
$ specific deploy --secret db_url=postgres://... --config domain=app.com`).action((options2) => {
|
|
@@ -378419,10 +378477,11 @@ Examples:
|
|
|
378419
378477
|
program.command("status").description("Show project, environments, and deployment status").addHelpText("after", `
|
|
378420
378478
|
Examples:
|
|
378421
378479
|
$ specific status`).action(statusCommand);
|
|
378422
|
-
program.command("query [sql]").description("Run a SQL query against your environment's observability data").option("--
|
|
378480
|
+
program.command("query [sql]").description("Run a SQL query against your environment's observability data").option("-e, --environment <name|id>", "Target environment (defaults to the current one)").addHelpText("after", `
|
|
378423
378481
|
Examples:
|
|
378424
378482
|
$ specific query "SELECT count() FROM observability.logs"
|
|
378425
|
-
$ specific query --
|
|
378483
|
+
$ specific query --environment staging "SELECT * FROM observability.logs LIMIT 5"
|
|
378484
|
+
$ specific query -e env_0abc123 "SELECT * FROM observability.logs LIMIT 5"
|
|
378426
378485
|
$ cat queries/p99.sql | specific query
|
|
378427
378486
|
$ specific query - < queries/p99.sql`).action((sql, options2) => {
|
|
378428
378487
|
queryCommand(sql, options2);
|
package/package.json
CHANGED
/package/dist/admin/_next/static/{pHCjR5T3AY4B3i9-nU0ND → dIbCgE-_TKsqFpoght8OM}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{pHCjR5T3AY4B3i9-nU0ND → dIbCgE-_TKsqFpoght8OM}/_ssgManifest.js
RENAMED
|
File without changes
|