naro-cli 0.26.0 → 0.28.0
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/README.md +59 -9
- package/dist/args.d.ts +2 -0
- package/dist/artifact-types.d.ts +1 -1
- package/dist/{chunk-RQJTCRKY.js → chunk-UJGDKNDM.js} +721 -978
- package/dist/deploy.d.ts +11 -2
- package/dist/deploy.js +7 -1
- package/dist/index.js +134 -60
- package/dist/user-adapter.d.ts +32 -0
- package/dist/working-tree.d.ts +1 -1
- package/package.json +5 -6
package/dist/deploy.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type CacheTransport, type LocalBuildDependencies } from "@next-deploy/b
|
|
|
2
2
|
import { type CliGitConnection } from "@next-deploy/sdk";
|
|
3
3
|
import type { BuildObserver, DeploymentArtifact, DeploymentStatus, DeploymentStrategyRecord } from "./artifact-types";
|
|
4
4
|
import { type GitInfo } from "./git-info";
|
|
5
|
+
import { type UserAdapterChoice } from "./user-adapter";
|
|
5
6
|
import { type WorkingTreeCopy } from "./working-tree";
|
|
6
7
|
/** The slice of LocalBuildExecutor the deploy flow needs — injectable in tests. */
|
|
7
8
|
export interface DeployBuildExecutor {
|
|
@@ -14,6 +15,7 @@ export interface DeployBuildExecutor {
|
|
|
14
15
|
env: Record<string, string>;
|
|
15
16
|
strategy?: DeploymentStrategyRecord | null;
|
|
16
17
|
vinextEnabled?: boolean;
|
|
18
|
+
userAdapter?: "replace" | "keep" | null;
|
|
17
19
|
}, observer: BuildObserver, cache?: CacheTransport): Promise<{
|
|
18
20
|
artifact: DeploymentArtifact;
|
|
19
21
|
}>;
|
|
@@ -75,7 +77,7 @@ export interface DeployOptions {
|
|
|
75
77
|
* enough to time some out when the whole suite ran at once.
|
|
76
78
|
*/
|
|
77
79
|
pollIntervalMs?: number;
|
|
78
|
-
/** Test seam: replaces the local
|
|
80
|
+
/** Test seam: replaces the local build. */
|
|
79
81
|
executor?: DeployBuildExecutor;
|
|
80
82
|
/** Test seam: replaces the `git` calls that read the repository. */
|
|
81
83
|
gitInfo?: (dir: string) => Promise<GitInfo>;
|
|
@@ -84,6 +86,12 @@ export interface DeployOptions {
|
|
|
84
86
|
* 대화형 CLI만 전달한다 — MCP·CI는 생략하고 안내만 받는다.
|
|
85
87
|
*/
|
|
86
88
|
onMissingEnv?: (keys: string[]) => Promise<Record<string, string>>;
|
|
89
|
+
/**
|
|
90
|
+
* The answer to the adapter question (`--user-adapter`, the prompt, MCP's
|
|
91
|
+
* userAdapter), saved to the project once the deployment exists when it
|
|
92
|
+
* differs from the stored choice (spec 2026-11-07 S3).
|
|
93
|
+
*/
|
|
94
|
+
userAdapter?: UserAdapterChoice | undefined;
|
|
87
95
|
}
|
|
88
96
|
export interface DeployResult {
|
|
89
97
|
deploymentId: string;
|
|
@@ -105,7 +113,7 @@ export interface DeployResult {
|
|
|
105
113
|
}
|
|
106
114
|
/**
|
|
107
115
|
* The full CLI deploy: authenticate, register the deployment with the
|
|
108
|
-
* control plane, build locally (
|
|
116
|
+
* control plane, build locally (vinext for Next.js, the static pipeline
|
|
109
117
|
* for Vite/CRA/static sites), upload the artifact and poll until the
|
|
110
118
|
* platform finishes publishing the worker.
|
|
111
119
|
*
|
|
@@ -115,3 +123,4 @@ export declare function deployDirectory(sourceDir: string, options: DeployOption
|
|
|
115
123
|
export { lintDatabase, type DatabaseLint } from "./commands/db-lint";
|
|
116
124
|
export { readCapped } from "./functions/read-capped";
|
|
117
125
|
export { assertFunctionsNode, deployFunctions, type DeployedFunction, type DeployFunctionsOptions, type DeployFunctionsResult, } from "./functions/deploy";
|
|
126
|
+
export { detectUserAdapter, parseUserAdapter, userAdapterUnreadable, type DetectedUserAdapter, type UserAdapterChoice, } from "./user-adapter";
|
package/dist/deploy.js
CHANGED
|
@@ -3,26 +3,32 @@ import {
|
|
|
3
3
|
assertFunctionsNode,
|
|
4
4
|
deployDirectory,
|
|
5
5
|
deployFunctions,
|
|
6
|
+
detectUserAdapter,
|
|
6
7
|
eventsObserver,
|
|
7
8
|
lintDatabase,
|
|
8
9
|
localDependencies,
|
|
9
10
|
parseDotenv,
|
|
11
|
+
parseUserAdapter,
|
|
10
12
|
readCapped,
|
|
11
13
|
readProjectEnv,
|
|
12
14
|
slugify,
|
|
15
|
+
userAdapterUnreadable,
|
|
13
16
|
workingTreeNotices
|
|
14
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UJGDKNDM.js";
|
|
15
18
|
import "./chunk-K3NQKI34.js";
|
|
16
19
|
export {
|
|
17
20
|
assertFunctionsNode,
|
|
18
21
|
deployDirectory,
|
|
19
22
|
deployFunctions,
|
|
23
|
+
detectUserAdapter,
|
|
20
24
|
eventsObserver,
|
|
21
25
|
lintDatabase,
|
|
22
26
|
localDependencies,
|
|
23
27
|
parseDotenv,
|
|
28
|
+
parseUserAdapter,
|
|
24
29
|
readCapped,
|
|
25
30
|
readProjectEnv,
|
|
26
31
|
slugify,
|
|
32
|
+
userAdapterUnreadable,
|
|
27
33
|
workingTreeNotices
|
|
28
34
|
};
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
STORAGE_OBJECTS_TABLE,
|
|
25
25
|
STORAGE_SUBQUERIES_SQL,
|
|
26
26
|
UNSHOWN_WARNING_LINE,
|
|
27
|
+
USER_ADAPTER_OPTIONS,
|
|
27
28
|
appliedMigrations,
|
|
28
29
|
applyMigrationFiles,
|
|
29
30
|
assertFunctionsNode,
|
|
@@ -48,6 +49,7 @@ import {
|
|
|
48
49
|
deployDirectory,
|
|
49
50
|
deployFunctions,
|
|
50
51
|
detectGitInfo,
|
|
52
|
+
detectUserAdapter,
|
|
51
53
|
enableBackendCommand,
|
|
52
54
|
ensureGitignoreHasNaro,
|
|
53
55
|
envFlag,
|
|
@@ -81,6 +83,7 @@ import {
|
|
|
81
83
|
parsePolicyNode,
|
|
82
84
|
parsePolicyStatement,
|
|
83
85
|
parseRedirectEntry,
|
|
86
|
+
parseUserAdapter,
|
|
84
87
|
pendingMigrationNames,
|
|
85
88
|
policiesForTypes,
|
|
86
89
|
policiesLostByReset,
|
|
@@ -124,12 +127,16 @@ import {
|
|
|
124
127
|
toNaroError,
|
|
125
128
|
unreadablePolicyComment,
|
|
126
129
|
usageMonthOf,
|
|
130
|
+
userAdapterQuestion,
|
|
131
|
+
userAdapterStored,
|
|
132
|
+
userAdapterUnasked,
|
|
133
|
+
userAdapterUnreadable,
|
|
127
134
|
userForAccessToken,
|
|
128
135
|
version,
|
|
129
136
|
warningLines,
|
|
130
137
|
withFunctionRetries,
|
|
131
138
|
writeProjectLink
|
|
132
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-UJGDKNDM.js";
|
|
133
140
|
import "./chunk-K3NQKI34.js";
|
|
134
141
|
|
|
135
142
|
// src/args.ts
|
|
@@ -257,6 +264,7 @@ var STRING_FLAGS = {
|
|
|
257
264
|
"--build-command": "buildCommand",
|
|
258
265
|
"--output-dir": "outputDir",
|
|
259
266
|
"--vinext": "vinext",
|
|
267
|
+
"--user-adapter": "userAdapter",
|
|
260
268
|
"--expires": "expires",
|
|
261
269
|
"--verify-jwt": "verifyJwt",
|
|
262
270
|
"--method": "method",
|
|
@@ -4207,6 +4215,7 @@ function deployTarget(flags) {
|
|
|
4207
4215
|
async function runDeploy(context, deps = { deploy: deployDirectory, offerAutoDeploy }) {
|
|
4208
4216
|
const { flags, output } = context;
|
|
4209
4217
|
const target = deployTarget(flags);
|
|
4218
|
+
const flaggedAdapter = flaggedUserAdapter(flags);
|
|
4210
4219
|
const link = flags.project ? { projectId: flags.project, teamSlug: void 0 } : readProjectLink(context.cwd);
|
|
4211
4220
|
if (target.production && !flags.yes && !flags.json && isInteractive()) {
|
|
4212
4221
|
const confirmed = await context.prompter.confirm("Deploy to production?");
|
|
@@ -4220,6 +4229,12 @@ async function runDeploy(context, deps = { deploy: deployDirectory, offerAutoDep
|
|
|
4220
4229
|
const account = await accountLabel(context);
|
|
4221
4230
|
output.log(` ${account} \u2192 ${team.slug}
|
|
4222
4231
|
`);
|
|
4232
|
+
const userAdapter = await askUserAdapter(
|
|
4233
|
+
context,
|
|
4234
|
+
flaggedAdapter,
|
|
4235
|
+
link,
|
|
4236
|
+
canPrompt
|
|
4237
|
+
);
|
|
4223
4238
|
const result = await deps.deploy(
|
|
4224
4239
|
context.cwd,
|
|
4225
4240
|
{
|
|
@@ -4246,7 +4261,8 @@ async function runDeploy(context, deps = { deploy: deployDirectory, offerAutoDep
|
|
|
4246
4261
|
)
|
|
4247
4262
|
},
|
|
4248
4263
|
token: flags.token,
|
|
4249
|
-
allowLogin: !flags.json && !flags.yes
|
|
4264
|
+
allowLogin: !flags.json && !flags.yes,
|
|
4265
|
+
...userAdapter ? { userAdapter } : {}
|
|
4250
4266
|
},
|
|
4251
4267
|
(message) => output.log(message)
|
|
4252
4268
|
);
|
|
@@ -4290,6 +4306,44 @@ async function runDeploy(context, deps = { deploy: deployDirectory, offerAutoDep
|
|
|
4290
4306
|
]
|
|
4291
4307
|
);
|
|
4292
4308
|
}
|
|
4309
|
+
function flaggedUserAdapter(flags) {
|
|
4310
|
+
if (flags.userAdapter === void 0) return void 0;
|
|
4311
|
+
const choice = parseUserAdapter(flags.userAdapter);
|
|
4312
|
+
if (!choice)
|
|
4313
|
+
throw new NaroError(
|
|
4314
|
+
"VALIDATION_FAILED",
|
|
4315
|
+
"--user-adapter takes replace or keep."
|
|
4316
|
+
);
|
|
4317
|
+
return choice;
|
|
4318
|
+
}
|
|
4319
|
+
async function askUserAdapter(context, flagged, link, canPrompt) {
|
|
4320
|
+
const { flags, output } = context;
|
|
4321
|
+
if (flagged) return flagged;
|
|
4322
|
+
const adapter = await detectUserAdapter(context.cwd, flags.root ?? null);
|
|
4323
|
+
if (!adapter) return void 0;
|
|
4324
|
+
let stored = null;
|
|
4325
|
+
if (link?.projectId) {
|
|
4326
|
+
try {
|
|
4327
|
+
stored = (await (await context.client()).projects.get(link.projectId)).userAdapter;
|
|
4328
|
+
} catch {
|
|
4329
|
+
output.log(userAdapterUnreadable(adapter));
|
|
4330
|
+
return void 0;
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
if (stored) {
|
|
4334
|
+
output.log(userAdapterStored(adapter, stored));
|
|
4335
|
+
return void 0;
|
|
4336
|
+
}
|
|
4337
|
+
if (!canPrompt) {
|
|
4338
|
+
output.log(userAdapterUnasked(adapter));
|
|
4339
|
+
return void 0;
|
|
4340
|
+
}
|
|
4341
|
+
const index = await context.prompter.select(
|
|
4342
|
+
userAdapterQuestion(adapter),
|
|
4343
|
+
USER_ADAPTER_OPTIONS.map((option) => option.label)
|
|
4344
|
+
);
|
|
4345
|
+
return USER_ADAPTER_OPTIONS[index]?.value ?? "replace";
|
|
4346
|
+
}
|
|
4293
4347
|
async function resolveDestination(context, link, canPrompt) {
|
|
4294
4348
|
const client = await context.client();
|
|
4295
4349
|
const teams = await client.teams.list();
|
|
@@ -11934,53 +11988,8 @@ async function runObservability(context) {
|
|
|
11934
11988
|
context.output.result({ projectId, report }, observabilityLines(report));
|
|
11935
11989
|
}
|
|
11936
11990
|
|
|
11937
|
-
// src/commands/supabase.ts
|
|
11938
|
-
async function runSupabase(context) {
|
|
11939
|
-
const projectId = await context.resolveProjectId();
|
|
11940
|
-
const client = await context.client();
|
|
11941
|
-
const links = await client.supabase.links(projectId);
|
|
11942
|
-
context.output.result({ projectId, links }, [
|
|
11943
|
-
`Supabase links (${String(links.length)}):`,
|
|
11944
|
-
...links.length === 0 ? [
|
|
11945
|
-
" None. Connect a Supabase project in the dashboard, under the project's Settings."
|
|
11946
|
-
] : links.map(
|
|
11947
|
-
(link) => ` ${link.target.padEnd(12)} ${link.supabaseName} (${link.supabaseRef}), ${link.keyKind} keys${link.includeSecret ? " + secret" : ""}`
|
|
11948
|
-
)
|
|
11949
|
-
]);
|
|
11950
|
-
}
|
|
11951
|
-
async function runSupabaseDisconnect(context) {
|
|
11952
|
-
const [given] = context.positionals;
|
|
11953
|
-
const target = given === "all" ? void 0 : given;
|
|
11954
|
-
const projectId = await context.resolveProjectId();
|
|
11955
|
-
const client = await context.client();
|
|
11956
|
-
await confirmOrAbort(
|
|
11957
|
-
context,
|
|
11958
|
-
target === void 0 ? "Disconnect Supabase from every target your role may change? The env vars it set are deleted, and the next deploy goes without them." : `Disconnect Supabase from ${target}? The env vars it set there are deleted, and the next deploy goes without them.`
|
|
11959
|
-
);
|
|
11960
|
-
const { removed } = await client.supabase.disconnect(
|
|
11961
|
-
projectId,
|
|
11962
|
-
target === void 0 ? {} : { target }
|
|
11963
|
-
);
|
|
11964
|
-
let remaining = [];
|
|
11965
|
-
if (target === void 0) {
|
|
11966
|
-
try {
|
|
11967
|
-
remaining = (await client.supabase.links(projectId)).map(
|
|
11968
|
-
(link) => link.target
|
|
11969
|
-
);
|
|
11970
|
-
} catch {
|
|
11971
|
-
remaining = null;
|
|
11972
|
-
}
|
|
11973
|
-
}
|
|
11974
|
-
context.output.result({ projectId, removed, remaining }, [
|
|
11975
|
-
`\u2705 Removed ${String(removed)} Supabase link(s)`,
|
|
11976
|
-
...remaining === null ? ["\u26A0 Could not read which links are left; run `naro supabase` to check."] : remaining.length > 0 ? [
|
|
11977
|
-
`\u26A0 Still connected: ${remaining.join(", ")} \u2014 your role cannot change those.`
|
|
11978
|
-
] : []
|
|
11979
|
-
]);
|
|
11980
|
-
}
|
|
11981
|
-
|
|
11982
11991
|
// src/commands/project-settings.ts
|
|
11983
|
-
var SET_USAGE2 = "Usage: naro project set [--subdomain <name>] [--framework <preset>] [--root <dir>] [--branch <branch>] [--build-command <script>] [--output-dir <dir>] [--vinext on|off]";
|
|
11992
|
+
var SET_USAGE2 = "Usage: naro project set [--subdomain <name>] [--framework <preset>] [--root <dir>] [--branch <branch>] [--build-command <script>] [--output-dir <dir>] [--vinext on|off] [--user-adapter replace|keep]";
|
|
11984
11993
|
var GRANT_USAGE = "Usage: naro project access grant <user-id> --role <project_admin|project_developer|project_viewer>";
|
|
11985
11994
|
function clearable(value) {
|
|
11986
11995
|
return value === "" ? null : value;
|
|
@@ -12002,6 +12011,15 @@ function settingsFrom(context) {
|
|
|
12002
12011
|
throw new NaroError("VALIDATION_FAILED", "--vinext takes on or off.");
|
|
12003
12012
|
patch.vinextEnabled = flags.vinext === "on";
|
|
12004
12013
|
}
|
|
12014
|
+
if (flags.userAdapter !== void 0) {
|
|
12015
|
+
const choice = parseUserAdapter(flags.userAdapter);
|
|
12016
|
+
if (!choice)
|
|
12017
|
+
throw new NaroError(
|
|
12018
|
+
"VALIDATION_FAILED",
|
|
12019
|
+
"--user-adapter takes replace or keep."
|
|
12020
|
+
);
|
|
12021
|
+
patch.userAdapter = choice;
|
|
12022
|
+
}
|
|
12005
12023
|
if (Object.keys(patch).length === 0)
|
|
12006
12024
|
throw new NaroError("VALIDATION_FAILED", SET_USAGE2);
|
|
12007
12025
|
return patch;
|
|
@@ -14098,6 +14116,53 @@ function usageLines3(report, summary) {
|
|
|
14098
14116
|
);
|
|
14099
14117
|
return lines;
|
|
14100
14118
|
}
|
|
14119
|
+
function tenths(value) {
|
|
14120
|
+
return Math.round(value * 10) / 10;
|
|
14121
|
+
}
|
|
14122
|
+
function cpuText(ms) {
|
|
14123
|
+
const whole = Math.round(ms);
|
|
14124
|
+
if (whole < 1e3) return `${String(whole)} ms`;
|
|
14125
|
+
const seconds = tenths(ms / 1e3);
|
|
14126
|
+
if (seconds < 60) return `${seconds.toFixed(1)} s`;
|
|
14127
|
+
const minutes = tenths(ms / 6e4);
|
|
14128
|
+
if (minutes < 60) return `${minutes.toFixed(1)} min`;
|
|
14129
|
+
return `${tenths(ms / 36e5).toFixed(1)} h`;
|
|
14130
|
+
}
|
|
14131
|
+
function environmentName(environment) {
|
|
14132
|
+
switch (environment.kind) {
|
|
14133
|
+
case "production":
|
|
14134
|
+
return "production";
|
|
14135
|
+
case "preview":
|
|
14136
|
+
return "preview";
|
|
14137
|
+
case "environment":
|
|
14138
|
+
return printable(environment.label);
|
|
14139
|
+
case "former":
|
|
14140
|
+
return `earlier name ${printable(environment.workerName)}`;
|
|
14141
|
+
}
|
|
14142
|
+
}
|
|
14143
|
+
function hostingLines(hosting) {
|
|
14144
|
+
if (!hosting || hosting.projects.length === 0) return [];
|
|
14145
|
+
const rows = [["CPU", "Invocations", "Project / environment"]];
|
|
14146
|
+
for (const project of hosting.projects) {
|
|
14147
|
+
rows.push([
|
|
14148
|
+
cpuText(project.cpuMs),
|
|
14149
|
+
formatCount(project.invocations),
|
|
14150
|
+
project.name === null ? `Not in this team ${printable(project.projectId)}` : printable(project.name)
|
|
14151
|
+
]);
|
|
14152
|
+
for (const environment of project.environments)
|
|
14153
|
+
rows.push([
|
|
14154
|
+
cpuText(environment.cpuMs),
|
|
14155
|
+
formatCount(environment.invocations),
|
|
14156
|
+
` ${environmentName(environment)}`
|
|
14157
|
+
]);
|
|
14158
|
+
}
|
|
14159
|
+
return [
|
|
14160
|
+
"",
|
|
14161
|
+
"Compute (CPU) \xB7 observed, not billed \xB7 sampled estimate",
|
|
14162
|
+
...table(rows),
|
|
14163
|
+
`Total: ${cpuText(hosting.totals.cpuMs)} of CPU over ${formatCount(hosting.totals.invocations)} invocations`
|
|
14164
|
+
];
|
|
14165
|
+
}
|
|
14101
14166
|
|
|
14102
14167
|
// src/commands/usage.ts
|
|
14103
14168
|
async function runUsage(context) {
|
|
@@ -14105,12 +14170,26 @@ async function runUsage(context) {
|
|
|
14105
14170
|
const teams = await client.teams.list();
|
|
14106
14171
|
const team = await resolveTeam(context, teams, { intent: "read" });
|
|
14107
14172
|
const month = context.flags.month;
|
|
14108
|
-
const
|
|
14109
|
-
|
|
14110
|
-
month !== void 0 ? { month } : {}
|
|
14111
|
-
);
|
|
14173
|
+
const query = month !== void 0 ? { month } : {};
|
|
14174
|
+
const report = await client.teams.usage(team.slug, query);
|
|
14112
14175
|
const summary = summarizeTeamUsage(report, usageMonthOf(/* @__PURE__ */ new Date()));
|
|
14113
|
-
|
|
14176
|
+
let hosting = null;
|
|
14177
|
+
let hostingError;
|
|
14178
|
+
try {
|
|
14179
|
+
hosting = await client.teams.usageHosting(team.slug, query);
|
|
14180
|
+
} catch (error) {
|
|
14181
|
+
hostingError = {
|
|
14182
|
+
code: error instanceof NaroError ? error.code : "UNKNOWN",
|
|
14183
|
+
message: error instanceof Error ? error.message : String(error)
|
|
14184
|
+
};
|
|
14185
|
+
context.output.warn(
|
|
14186
|
+
`Could not read compute usage: ${hostingError.message}`
|
|
14187
|
+
);
|
|
14188
|
+
}
|
|
14189
|
+
context.output.result(
|
|
14190
|
+
hostingError ? { ...report, summary, hosting, hostingError } : { ...report, summary, hosting },
|
|
14191
|
+
[...usageLines3(report, summary), ...hostingLines(hosting)]
|
|
14192
|
+
);
|
|
14114
14193
|
}
|
|
14115
14194
|
|
|
14116
14195
|
// src/router.ts
|
|
@@ -14143,6 +14222,7 @@ Usage:
|
|
|
14143
14222
|
naro project delete Delete the project and everything under it: type its name to confirm
|
|
14144
14223
|
naro project set [--subdomain <name>] [--framework <preset>] [--root <dir>] [--branch <branch>]
|
|
14145
14224
|
[--build-command <script>] [--output-dir <dir>] [--vinext on|off]
|
|
14225
|
+
[--user-adapter replace|keep]
|
|
14146
14226
|
Change the General settings; an empty value clears one. A new subdomain
|
|
14147
14227
|
moves the project's address at once (asks first; --yes in CI)
|
|
14148
14228
|
naro project check <subdomain> Whether a subdomain is free
|
|
@@ -14152,7 +14232,7 @@ Usage:
|
|
|
14152
14232
|
Take a contributor's project role away (asks first)
|
|
14153
14233
|
[--project <id> --yes] (in CI both; --yes alone is refused so the folder cannot choose the project)
|
|
14154
14234
|
naro usage [--month YYYY-MM] [--team <slug>]
|
|
14155
|
-
Team usage: requests, builds and custom domains against the plan, then by project
|
|
14235
|
+
Team usage: requests, builds and custom domains against the plan, then by project, then CPU by project and environment
|
|
14156
14236
|
naro billing [--team <slug>] The team's plan, subscription and this month's overage; the plan changes in the dashboard
|
|
14157
14237
|
naro dashboard [<capability>] The dashboard URL for what the CLI leaves there (payments, GitHub App, \u2026); no argument lists them
|
|
14158
14238
|
|
|
@@ -14195,8 +14275,6 @@ Usage:
|
|
|
14195
14275
|
naro env import <file> [target] Upload a dotenv file's variables (asks first; --yes in CI)
|
|
14196
14276
|
naro env pull [file] Write env vars to .env.local
|
|
14197
14277
|
target is production, preview, all or an environment name; add and import use all when none is given
|
|
14198
|
-
naro supabase [list] The project's Supabase links: target, Supabase project and keys (connect in the dashboard)
|
|
14199
|
-
naro supabase disconnect [target] Remove a target's link, or (no target or all) every link your role may change, with its env vars (asks first)
|
|
14200
14278
|
|
|
14201
14279
|
naro environments List the project's environments: address, branch, protection, data, and how many the plan allows
|
|
14202
14280
|
naro environments create <slug> [--branch <b>] [--copy-env preview|production|none] [--database schema|empty]
|
|
@@ -14828,10 +14906,6 @@ function routeCommand(parsed) {
|
|
|
14828
14906
|
// Spec 2026-10-18-usage-cli-mcp §2.
|
|
14829
14907
|
case "usage":
|
|
14830
14908
|
return positionals.length === 0 ? { handler: runUsage, positionals: [] } : null;
|
|
14831
|
-
case "supabase":
|
|
14832
|
-
if (sub === void 0 || sub === "list")
|
|
14833
|
-
return positionals.length <= 1 ? { handler: runSupabase, positionals: [] } : null;
|
|
14834
|
-
return sub === "disconnect" && positionals.length <= 2 ? { handler: runSupabaseDisconnect, positionals: positionals.slice(1) } : null;
|
|
14835
14909
|
case "dashboard":
|
|
14836
14910
|
return positionals.length <= 1 ? { handler: runDashboard, positionals } : null;
|
|
14837
14911
|
case "billing":
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type UserAdapterChoice = "replace" | "keep";
|
|
2
|
+
export interface DetectedUserAdapter {
|
|
3
|
+
/** The active next.config, relative to the app directory. */
|
|
4
|
+
file: string;
|
|
5
|
+
key: "adapterPath" | "experimental.adapterPath";
|
|
6
|
+
}
|
|
7
|
+
/** The next.config Next would load sets adapterPath, read the way Naro's builders read it. */
|
|
8
|
+
export declare function detectUserAdapter(directory: string, rootDirectory: string | null): Promise<DetectedUserAdapter | null>;
|
|
9
|
+
export declare function parseUserAdapter(value: unknown): UserAdapterChoice | null;
|
|
10
|
+
export declare const USER_ADAPTER_OPTIONS: ReadonlyArray<{
|
|
11
|
+
value: UserAdapterChoice;
|
|
12
|
+
label: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function userAdapterQuestion(adapter: DetectedUserAdapter): string;
|
|
15
|
+
/** One line on what a choice means for this deploy (the local build has no Naro adapter, spec S2 D8). */
|
|
16
|
+
export declare function userAdapterOutcome(choice: UserAdapterChoice): string;
|
|
17
|
+
/** The line for a project that already chose. */
|
|
18
|
+
export declare function userAdapterStored(adapter: DetectedUserAdapter, choice: UserAdapterChoice): string;
|
|
19
|
+
/** The warning when nobody chose and nobody can be asked. */
|
|
20
|
+
export declare function userAdapterUnasked(adapter: DetectedUserAdapter): string;
|
|
21
|
+
/** Where each surface changes the stored choice. */
|
|
22
|
+
declare const CHOOSE_USER_ADAPTER: {
|
|
23
|
+
readonly cli: "naro project set --user-adapter replace|keep";
|
|
24
|
+
readonly mcp: "update_project with userAdapter replace|keep";
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The warning when the project's stored choice could not be read. It may
|
|
28
|
+
* already say keep, so this line must not claim replace the way
|
|
29
|
+
* userAdapterUnasked does.
|
|
30
|
+
*/
|
|
31
|
+
export declare function userAdapterUnreadable(adapter: DetectedUserAdapter, surface?: keyof typeof CHOOSE_USER_ADAPTER): string;
|
|
32
|
+
export {};
|
package/dist/working-tree.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export interface WorkingTreeCopy {
|
|
|
49
49
|
* edit to a committed `.env`, one hidden behind `skip-worktree`, one only
|
|
50
50
|
* staged, and an untracked `.env.local` all stay on the machine. `next build`
|
|
51
51
|
* reads those files on its own and inlines NEXT_PUBLIC_* into the bundle, and
|
|
52
|
-
*
|
|
52
|
+
* the bundler inlines every value they hold into the Worker, so a file copied
|
|
53
53
|
* here would ship values the developer declined to send. The env consent
|
|
54
54
|
* prompt is the only way local values reach a build (known-issues #7,
|
|
55
55
|
* CLI-DEPLOY-011).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naro-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"naro": "./dist/index.js"
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@cloudflare/unenv-preset": "2.16.2",
|
|
26
|
-
"@opennextjs/cloudflare": "1.20.12",
|
|
27
26
|
"@supabase/supabase-js": "2.112.3",
|
|
28
27
|
"@vercel/frameworks": "3.34.0",
|
|
29
28
|
"esbuild": "0.28.1",
|
|
@@ -37,14 +36,14 @@
|
|
|
37
36
|
"tsx": "4.23.12",
|
|
38
37
|
"typescript": "7.0.2",
|
|
39
38
|
"vitest": "3.2.7",
|
|
39
|
+
"naro-js": "0.9.0",
|
|
40
40
|
"@next-deploy/build": "0.1.0",
|
|
41
|
-
"@next-deploy/data-core": "0.1.0",
|
|
42
41
|
"@next-deploy/config": "0.1.0",
|
|
42
|
+
"@next-deploy/data-core": "0.1.0",
|
|
43
43
|
"@next-deploy/functions-runtime": "0.1.0",
|
|
44
|
-
"
|
|
44
|
+
"@next-deploy/functions-bundler": "0.1.0",
|
|
45
45
|
"@next-deploy/sdk": "0.1.0",
|
|
46
|
-
"@next-deploy/core": "0.1.0"
|
|
47
|
-
"@next-deploy/functions-bundler": "0.1.0"
|
|
46
|
+
"@next-deploy/core": "0.1.0"
|
|
48
47
|
},
|
|
49
48
|
"scripts": {
|
|
50
49
|
"build": "tsup && pnpm build:types",
|