neon 4.7.0 → 4.8.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 +37 -59
- package/dist/_chunks/{auth_selection-ktL6uoEe.js → auth_selection-pitMFzDe.js} +1 -1
- package/dist/_chunks/{credential_io-B1JxWELN.js → credential_io-YeAxg9Xn.js} +2 -2
- package/dist/_chunks/{paths-g3DRqJiD.js → paths-DMq0Lt7a.js} +1 -1
- package/dist/_chunks/{profiles-D95AeZ5v.js → profiles-CvnFEQyd.js} +1 -1
- package/dist/analytics.js +2 -2
- package/dist/auth_context.js +1 -1
- package/dist/commands/auth.js +12 -9
- package/dist/commands/bootstrap.js +72 -43
- package/dist/commands/claim.js +1 -1
- package/dist/commands/init.js +80 -79
- package/dist/commands/link.js +78 -63
- package/dist/commands/mcp.js +1 -1
- package/dist/commands/plugins.js +4 -4
- package/dist/commands/profile.js +4 -4
- package/dist/commands/skills.js +1 -1
- package/dist/config.js +1 -1
- package/dist/credential_io.js +1 -1
- package/dist/index.js +1 -1
- package/dist/init/child.js +36 -0
- package/dist/init/plan.js +47 -0
- package/dist/init/tooling.js +43 -0
- package/dist/init/wizard.js +42 -0
- package/dist/mcp/targets.js +1 -1
- package/dist/plugins/plan.js +1 -1
- package/dist/plugins/targets.js +5 -1
- package/dist/retire_credential.js +1 -1
- package/dist/utils/middlewares.js +1 -1
- package/dist/utils/package_manager.js +10 -5
- package/package.json +13 -14
- package/dist/init/auth.js +0 -53
- package/dist/init/build_config.js +0 -4
- package/dist/init/detect_agent.js +0 -63
- package/dist/init/editors.js +0 -19
- package/dist/init/enrich_output.js +0 -61
- package/dist/init/extension.js +0 -154
- package/dist/init/inspect.js +0 -249
- package/dist/init/interactive.js +0 -529
- package/dist/init/neonctl.js +0 -139
- package/dist/init/orchestrate.js +0 -189
- package/dist/init/phases/auth.js +0 -195
- package/dist/init/phases/cleanup.js +0 -27
- package/dist/init/phases/db.js +0 -257
- package/dist/init/phases/getting_started.js +0 -218
- package/dist/init/phases/mcp.js +0 -219
- package/dist/init/phases/migrations.js +0 -250
- package/dist/init/phases/neon_auth.js +0 -116
- package/dist/init/phases/setup.js +0 -582
- package/dist/init/phases/skills.js +0 -86
- package/dist/init/phases/status.js +0 -82
- package/dist/init/profile_cli.js +0 -56
- package/dist/init/resolve_context.js +0 -105
- package/dist/init/route_command.js +0 -93
- package/dist/init/skills.js +0 -206
- package/dist/init/types.js +0 -1
- package/dist/init/vsix.js +0 -89
package/dist/commands/init.js
CHANGED
|
@@ -1,94 +1,95 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
2
|
+
import { log } from "../log.js";
|
|
3
|
+
import { readContextFile } from "../context.js";
|
|
4
|
+
import { getCliName } from "../utils/cli_name.js";
|
|
5
|
+
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
6
|
+
import { initChildEnv, spawnCliChild } from "../init/child.js";
|
|
7
|
+
import { bootstrapInitStep, directoryIsEmpty, planExistingInit } from "../init/plan.js";
|
|
8
|
+
import { runAgentTooling, runInitSteps } from "../init/tooling.js";
|
|
9
|
+
import { helpEpilogue } from "../utils/help_text.js";
|
|
10
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
11
|
+
import { resolve } from "node:path";
|
|
9
12
|
//#region src/commands/init.ts
|
|
10
13
|
var init_exports = /* @__PURE__ */ __exportAll({
|
|
11
14
|
builder: () => builder,
|
|
12
15
|
command: () => command,
|
|
13
16
|
describe: () => describe,
|
|
14
|
-
handler: () => handler
|
|
17
|
+
handler: () => handler,
|
|
18
|
+
initChildEnv: () => initChildEnv
|
|
15
19
|
});
|
|
16
20
|
const command = "init";
|
|
17
|
-
const describe = "
|
|
18
|
-
const
|
|
19
|
-
|
|
21
|
+
const describe = "Set up this directory for Neon: agent tooling, a linked project, and neon.ts. In an empty directory, scaffolds a template first. Skills needs Node.js 22.20 or newer.";
|
|
22
|
+
const removedProtocol = () => `\`${getCliName()} init --agent\` and \`--data\` were removed. Run \`${getCliName()} init\` or \`${getCliName()} init -y\`.`;
|
|
23
|
+
const builder = (yargs) => yargs.usage("$0 init [options]").option("context-file", { hidden: true }).option("yes", {
|
|
24
|
+
alias: "y",
|
|
20
25
|
type: "boolean",
|
|
21
26
|
default: false,
|
|
22
|
-
describe: "
|
|
27
|
+
describe: "Empty dir: bootstrap --default. Otherwise plugin when a project plugin agent is detected, else skills and MCP, then link --yes and config init --services none. link --yes still asks for a project unless one is already linked"
|
|
28
|
+
}).option("agent", {
|
|
29
|
+
hidden: true,
|
|
30
|
+
type: "boolean"
|
|
23
31
|
}).option("data", {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}).option("
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
describe: "
|
|
30
|
-
}).
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}).strict(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* `INFO: ` and send it to stderr, which is right for a diagnostic and wrong for the
|
|
39
|
-
* payload an agent is expected to parse.
|
|
40
|
-
*/
|
|
41
|
-
const writeAgentResponse = (result) => {
|
|
42
|
-
writeAllSync(1, `${JSON.stringify(enrichResponse(result), null, 2)}\n`);
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* A failure has to arrive in the shape the caller asked for. An agent parses stdout and
|
|
46
|
-
* has no branch for "empty stdout, exit 1" — it cannot tell a broken credentials file
|
|
47
|
-
* from a phase that legitimately produced nothing — so the error goes out as JSON too.
|
|
48
|
-
*/
|
|
49
|
-
const writeAgentFailure = (error) => {
|
|
50
|
-
writeAllSync(1, `${JSON.stringify({
|
|
51
|
-
success: false,
|
|
52
|
-
error: error.message
|
|
53
|
-
}, null, 2)}\n`);
|
|
54
|
-
};
|
|
55
|
-
/** ` at position 12`, or nothing when the parser did not report one. */
|
|
56
|
-
const parsePosition = (parseError) => {
|
|
57
|
-
const at = (parseError instanceof Error ? parseError.message : "").match(/at position (\d+)/);
|
|
58
|
-
return at === null ? "" : ` at position ${at[1]}`;
|
|
32
|
+
hidden: true,
|
|
33
|
+
type: "string"
|
|
34
|
+
}).option("output", {
|
|
35
|
+
alias: "o",
|
|
36
|
+
hidden: true,
|
|
37
|
+
describe: "Not supported; the commands init runs print their own output"
|
|
38
|
+
}).example("$0 init", "Empty dir: bootstrap (scaffold, agent tooling, link). Existing app: agent tooling, link, config init").example("$0 init -y", "Same steps, using each child's defaults").epilogue(helpEpilogue("Interactive agent setup: plugin (recommended), skills and MCP separately, or skip agent setup. Never both plugin and skills+MCP.", "-y installs the plugin when Cursor, Claude Code, or Codex is detected in the project. Otherwise skills and MCP. Then link unless already linked. link --yes may still ask for a project.")).check((argv) => {
|
|
39
|
+
if (argv.agent === true || argv.data !== void 0) throw new Error(removedProtocol());
|
|
40
|
+
if (argv.help !== true && (argv.output === "json" || argv.output === "yaml")) throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
|
|
41
|
+
return true;
|
|
42
|
+
}).strict();
|
|
43
|
+
const isLinked = (contextFile) => {
|
|
44
|
+
const projectId = readContextFile(contextFile).projectId;
|
|
45
|
+
return typeof projectId === "string" && projectId.length > 0;
|
|
59
46
|
};
|
|
60
|
-
const handler = async (
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
sendError(cause, "NEON_INIT_FAILED");
|
|
86
|
-
writeAgentFailure(cause);
|
|
87
|
-
await closeAnalytics();
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
throw cause;
|
|
47
|
+
const handler = async (props) => {
|
|
48
|
+
if (props.output === "json" || props.output === "yaml") throw new Error(`\`${getCliName()} init\` does not support --output. The commands it runs print their own output.`);
|
|
49
|
+
const cwd = props.cwd ?? process.cwd();
|
|
50
|
+
const names = existsSync(cwd) ? readdirSync(cwd) : [];
|
|
51
|
+
const contextFile = resolve(cwd, props.contextFile);
|
|
52
|
+
const yes = props.yes === true;
|
|
53
|
+
const run = props.run ?? spawnCliChild;
|
|
54
|
+
const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
|
|
55
|
+
const authEnv = explicitKey ? { NEON_API_KEY: explicitKey } : void 0;
|
|
56
|
+
const forward = {
|
|
57
|
+
...props.configDir ? { configDir: props.configDir } : {},
|
|
58
|
+
...props.profile ? { profile: props.profile } : {},
|
|
59
|
+
apiHost: props.apiHost,
|
|
60
|
+
contextFile,
|
|
61
|
+
...props.analytics === false ? { analytics: false } : {}
|
|
62
|
+
};
|
|
63
|
+
if (directoryIsEmpty(names)) {
|
|
64
|
+
await runInitSteps([bootstrapInitStep(yes)], {
|
|
65
|
+
cwd,
|
|
66
|
+
run,
|
|
67
|
+
forward,
|
|
68
|
+
authEnv
|
|
69
|
+
});
|
|
70
|
+
log.info("Done.");
|
|
71
|
+
return;
|
|
91
72
|
}
|
|
73
|
+
await runAgentTooling({
|
|
74
|
+
cwd,
|
|
75
|
+
yes,
|
|
76
|
+
run,
|
|
77
|
+
forward,
|
|
78
|
+
authEnv,
|
|
79
|
+
...props.pickAgentSetup ? { pickAgentSetup: props.pickAgentSetup } : {},
|
|
80
|
+
...props.hasProjectPlugins ? { hasProjectPlugins: props.hasProjectPlugins } : {}
|
|
81
|
+
});
|
|
82
|
+
await runInitSteps(planExistingInit({
|
|
83
|
+
linked: isLinked(contextFile),
|
|
84
|
+
yes,
|
|
85
|
+
agentSetup: "skip"
|
|
86
|
+
}), {
|
|
87
|
+
cwd,
|
|
88
|
+
run,
|
|
89
|
+
forward,
|
|
90
|
+
authEnv
|
|
91
|
+
});
|
|
92
|
+
log.info("Done.");
|
|
92
93
|
};
|
|
93
94
|
//#endregion
|
|
94
|
-
export { builder, command, describe, handler, init_exports as t };
|
|
95
|
+
export { builder, command, describe, handler, initChildEnv, init_exports as t };
|
package/dist/commands/link.js
CHANGED
|
@@ -25,7 +25,7 @@ const nonInteractiveLinkCommands = () => {
|
|
|
25
25
|
return [
|
|
26
26
|
`${cli} orgs list --output json`,
|
|
27
27
|
`${cli} projects list --org-id <org-id> --output json`,
|
|
28
|
-
`${cli} link --project-id <project-id
|
|
28
|
+
`${cli} link --project-id <project-id> [--branch <name> | -y]`,
|
|
29
29
|
`${cli} link --org-id <org-id> --project-name <name> --region-id aws-us-east-2`
|
|
30
30
|
];
|
|
31
31
|
};
|
|
@@ -53,7 +53,7 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
|
53
53
|
},
|
|
54
54
|
branch: {
|
|
55
55
|
alias: "branch-id",
|
|
56
|
-
describe:
|
|
56
|
+
describe: "Branch name or ID to pin in the context (resolved to its name before writing).",
|
|
57
57
|
type: "string"
|
|
58
58
|
},
|
|
59
59
|
params: {
|
|
@@ -66,7 +66,7 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
|
66
66
|
},
|
|
67
67
|
yes: {
|
|
68
68
|
alias: "y",
|
|
69
|
-
describe: "Skip the \"already linked\" confirmation
|
|
69
|
+
describe: "Skip the \"already linked\" confirmation, and pin the project's default branch when linking a project that has more than one.",
|
|
70
70
|
type: "boolean",
|
|
71
71
|
default: false
|
|
72
72
|
},
|
|
@@ -86,7 +86,8 @@ const builder = (argv) => argv.usage("$0 link [options]").options({
|
|
|
86
86
|
default: true
|
|
87
87
|
}
|
|
88
88
|
}).example([
|
|
89
|
-
["$0 link --project-id polished-snowflake-12345678",
|
|
89
|
+
["$0 link --project-id polished-snowflake-12345678", "Link an existing project (org is inferred). Pins the only branch; several prompt in a TTY"],
|
|
90
|
+
["$0 link --project-id polished-snowflake-12345678 -y", "Same, pinning the project's default branch when several exist"],
|
|
90
91
|
["$0 link --org-id org-… --project-name my-app --region-id aws-us-east-2", "Create a new project and link it"],
|
|
91
92
|
["$0 link --branch-id br-…", "Pin a branch in the already-linked project"],
|
|
92
93
|
["$0 link --no-checks --org-id org-… --project-id polished-snowflake-12345678", "Write the context offline (no API calls, no verification)"],
|
|
@@ -166,16 +167,8 @@ const validateInputs = (inputs) => {
|
|
|
166
167
|
if (inputs.projectName && inputs.branch) throw new Error(`Conflicting inputs: --branch pins a branch of an existing project, but --project-name creates a new one. Create the project first, then \`${getCliName()} checkout <branch>\`.`);
|
|
167
168
|
};
|
|
168
169
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* interactive picker (TTY) or the CI guard.
|
|
172
|
-
*
|
|
173
|
-
* - `--project-id` is always enough: the org is inferred from the project and
|
|
174
|
-
* the branch is left to an explicit `checkout` (never auto-defaulted).
|
|
175
|
-
* - `--org-id --project-name --region-id` fully describes a project to create.
|
|
176
|
-
* - `--branch-id` needs a project, which it takes from the existing `.neon`.
|
|
177
|
-
* - `--org-id` on its own just records the default org (merged into any
|
|
178
|
-
* existing context).
|
|
170
|
+
* Branch selection may still prompt after org and project resolve without the
|
|
171
|
+
* org/project wizard.
|
|
179
172
|
*/
|
|
180
173
|
const canResolveNonInteractively = (inputs, existing) => {
|
|
181
174
|
if (inputs.projectId) return true;
|
|
@@ -295,20 +288,65 @@ const resolveOrgForProject = async (props, inputs, existing, projectId) => {
|
|
|
295
288
|
if (projectId === existing.projectId && existing.orgId) return existing.orgId;
|
|
296
289
|
};
|
|
297
290
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
* `link` never guesses the project's default branch — that's `checkout`'s job —
|
|
301
|
-
* so the only sources are an explicit `--branch` (name or id, verified and
|
|
302
|
-
* normalized to its name) or a branch already pinned for the *same* project (so
|
|
303
|
-
* re-linking it doesn't drop your checked-out branch). Reading the existing
|
|
304
|
-
* branch via {@link contextBranch} also recovers a legacy `branchId` field.
|
|
291
|
+
* Keep an existing pin when re-linking the same project so `-y` does not reset
|
|
292
|
+
* a checkout.
|
|
305
293
|
*/
|
|
306
294
|
const resolvePinnedBranch = async (props, inputs, existing, projectId) => {
|
|
307
295
|
if (inputs.branch) {
|
|
308
296
|
const branch = await resolveBranchRef(props, projectId, inputs.branch);
|
|
309
|
-
return
|
|
297
|
+
return {
|
|
298
|
+
branch: branchPersistValue(branch),
|
|
299
|
+
empty: false
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (projectId === existing.projectId) {
|
|
303
|
+
const pinned = contextBranch(existing);
|
|
304
|
+
if (pinned) return {
|
|
305
|
+
branch: pinned,
|
|
306
|
+
empty: false
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return resolveBranchFromList(props, projectId);
|
|
310
|
+
};
|
|
311
|
+
const resolveBranchFromList = async (props, projectId) => {
|
|
312
|
+
const { data } = await props.apiClient.listProjectBranches({ projectId });
|
|
313
|
+
const branches = data.branches;
|
|
314
|
+
if (branches.length === 0) return { empty: true };
|
|
315
|
+
if (branches.length === 1) {
|
|
316
|
+
const [only] = branches;
|
|
317
|
+
if (!only) return { empty: true };
|
|
318
|
+
return {
|
|
319
|
+
branch: branchPersistValue(only),
|
|
320
|
+
empty: false
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (props.yes) {
|
|
324
|
+
const def = branches.find((b) => b.default);
|
|
325
|
+
if (!def) throw new Error(`Project '${projectId}' has no default branch. Pass --branch <name> to pin one.`);
|
|
326
|
+
return {
|
|
327
|
+
branch: branchPersistValue(def),
|
|
328
|
+
empty: false
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (canPromptInteractively()) {
|
|
332
|
+
const picked = await pickBranchInteractively(branches, {
|
|
333
|
+
message: "Which branch would you like to link?",
|
|
334
|
+
nonInteractiveMessage: `No branch could be selected without an interactive terminal. Re-run \`${getCliName()} link\` interactively, or \`${getCliName()} checkout <branch>\` to pin one.`
|
|
335
|
+
});
|
|
336
|
+
if (picked.kind === "existing") {
|
|
337
|
+
const existing = branches.find((b) => b.id === picked.branchId);
|
|
338
|
+
return {
|
|
339
|
+
branch: existing ? branchPersistValue(existing) : picked.branchId,
|
|
340
|
+
empty: false
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
await createBranch(props.apiClient, projectId, picked.name, branches);
|
|
344
|
+
return {
|
|
345
|
+
branch: picked.name,
|
|
346
|
+
empty: false
|
|
347
|
+
};
|
|
310
348
|
}
|
|
311
|
-
|
|
349
|
+
return { empty: false };
|
|
312
350
|
};
|
|
313
351
|
const runNonInteractive = async (props, inputs, existing) => {
|
|
314
352
|
if (inputs.projectName) {
|
|
@@ -337,36 +375,38 @@ const runNonInteractive = async (props, inputs, existing) => {
|
|
|
337
375
|
}
|
|
338
376
|
if (inputs.projectId) {
|
|
339
377
|
const orgId = await resolveOrgForProject(props, inputs, existing, inputs.projectId);
|
|
340
|
-
const
|
|
378
|
+
const resolved = await resolvePinnedBranch(props, inputs, existing, inputs.projectId);
|
|
341
379
|
applyContext(props.contextFile, {
|
|
342
380
|
orgId,
|
|
343
381
|
projectId: inputs.projectId,
|
|
344
|
-
branch
|
|
382
|
+
branch: resolved.branch
|
|
345
383
|
});
|
|
346
384
|
await finalizeLink(props, {
|
|
347
385
|
contextFile: props.contextFile,
|
|
348
386
|
orgId,
|
|
349
387
|
projectId: inputs.projectId,
|
|
350
|
-
branch,
|
|
351
|
-
created: false
|
|
388
|
+
branch: resolved.branch,
|
|
389
|
+
created: false,
|
|
390
|
+
empty: resolved.empty
|
|
352
391
|
});
|
|
353
392
|
return;
|
|
354
393
|
}
|
|
355
394
|
if (inputs.branch && existing.projectId) {
|
|
356
395
|
const projectId = existing.projectId;
|
|
357
396
|
const orgId = await resolveOrgForProject(props, inputs, existing, projectId);
|
|
358
|
-
const
|
|
397
|
+
const resolved = await resolvePinnedBranch(props, inputs, existing, projectId);
|
|
359
398
|
applyContext(props.contextFile, {
|
|
360
399
|
orgId,
|
|
361
400
|
projectId,
|
|
362
|
-
branch
|
|
401
|
+
branch: resolved.branch
|
|
363
402
|
});
|
|
364
403
|
await finalizeLink(props, {
|
|
365
404
|
contextFile: props.contextFile,
|
|
366
405
|
orgId,
|
|
367
406
|
projectId,
|
|
368
|
-
branch,
|
|
369
|
-
created: false
|
|
407
|
+
branch: resolved.branch,
|
|
408
|
+
created: false,
|
|
409
|
+
empty: resolved.empty
|
|
370
410
|
});
|
|
371
411
|
return;
|
|
372
412
|
}
|
|
@@ -425,20 +465,21 @@ const runInteractive = async (props, inputs) => {
|
|
|
425
465
|
const projects = await listAllProjects(props, orgId);
|
|
426
466
|
const action = await promptProjectChoice(projects, inputs.projectName);
|
|
427
467
|
if (action.type === "existing") {
|
|
428
|
-
const
|
|
468
|
+
const resolved = await resolveBranchFromList(props, action.projectId);
|
|
429
469
|
applyContext(props.contextFile, {
|
|
430
470
|
orgId,
|
|
431
471
|
projectId: action.projectId,
|
|
432
|
-
branch
|
|
472
|
+
branch: resolved.branch
|
|
433
473
|
});
|
|
434
474
|
await finalizeInteractiveLink(props, {
|
|
435
475
|
contextFile: props.contextFile,
|
|
436
476
|
orgId,
|
|
437
477
|
projectId: action.projectId,
|
|
438
|
-
branch,
|
|
478
|
+
branch: resolved.branch,
|
|
439
479
|
created: false,
|
|
440
480
|
projectName: action.name,
|
|
441
|
-
regionId: action.regionId
|
|
481
|
+
regionId: action.regionId,
|
|
482
|
+
empty: resolved.empty
|
|
442
483
|
});
|
|
443
484
|
return;
|
|
444
485
|
}
|
|
@@ -626,33 +667,6 @@ const listAllProjects = async (props, orgId) => {
|
|
|
626
667
|
}
|
|
627
668
|
return result;
|
|
628
669
|
};
|
|
629
|
-
/**
|
|
630
|
-
* Resolve which branch to pin for an interactively-chosen project, returned as the value to
|
|
631
|
-
* persist (its name when known, see {@link branchPersistValue}). When the project has a
|
|
632
|
-
* single branch there is nothing to choose, so we pin it silently. Otherwise we offer the
|
|
633
|
-
* shared branch picker (the same "+ Create a new branch…" + list as `neonctl checkout`),
|
|
634
|
-
* creating the branch when the user opts to. This makes interactive `link` a full org →
|
|
635
|
-
* project → branch flow; non-interactive `link` instead defers the branch to `checkout`.
|
|
636
|
-
*/
|
|
637
|
-
const resolveInteractiveBranch = async (props, projectId) => {
|
|
638
|
-
const { data } = await props.apiClient.listProjectBranches({ projectId });
|
|
639
|
-
const branches = data.branches;
|
|
640
|
-
if (branches.length <= 1) {
|
|
641
|
-
const only = branches.find((b) => b.default) ?? branches[0];
|
|
642
|
-
if (!only) throw new Error(`Could not find a default branch for project ${projectId}.`);
|
|
643
|
-
return branchPersistValue(only);
|
|
644
|
-
}
|
|
645
|
-
const picked = await pickBranchInteractively(branches, {
|
|
646
|
-
message: "Which branch would you like to link?",
|
|
647
|
-
nonInteractiveMessage: `No branch could be selected without an interactive terminal. Re-run \`${getCliName()} link\` interactively, or \`${getCliName()} checkout <branch>\` to pin one.`
|
|
648
|
-
});
|
|
649
|
-
if (picked.kind === "existing") {
|
|
650
|
-
const existing = branches.find((b) => b.id === picked.branchId);
|
|
651
|
-
return existing ? branchPersistValue(existing) : picked.branchId;
|
|
652
|
-
}
|
|
653
|
-
await createBranch(props.apiClient, projectId, picked.name, branches);
|
|
654
|
-
return picked.name;
|
|
655
|
-
};
|
|
656
670
|
const fetchRegions = async (props) => {
|
|
657
671
|
try {
|
|
658
672
|
const { data } = await props.apiClient.getActiveRegions();
|
|
@@ -704,7 +718,8 @@ const printSummary = (_props, summary) => {
|
|
|
704
718
|
lines.push("Written offline (--no-checks): nothing was verified.");
|
|
705
719
|
} else if (summary.projectId && !summary.branch && !summary.orgOnly) {
|
|
706
720
|
lines.push("");
|
|
707
|
-
lines.push(`
|
|
721
|
+
if (summary.empty) lines.push(`This project has no branches, so none was pinned. Create a branch, then run \`${getCliName()} checkout <branch>\` to pin it and pull its env vars.`);
|
|
722
|
+
else lines.push(`No branch pinned. Run \`${getCliName()} checkout <branch>\` to pin a branch and pull its env vars.`);
|
|
708
723
|
}
|
|
709
724
|
lines.push("");
|
|
710
725
|
process.stdout.write(`${lines.join("\n")}\n`);
|
package/dist/commands/mcp.js
CHANGED
|
@@ -4,10 +4,10 @@ import { readContextFile } from "../context.js";
|
|
|
4
4
|
import { getCliName } from "../utils/cli_name.js";
|
|
5
5
|
import { writer } from "../writer.js";
|
|
6
6
|
import { noPassthrough, single } from "../utils/flags.js";
|
|
7
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
7
8
|
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
8
9
|
import { NEON_MCP_CATEGORIES, NEON_MCP_URL, existingNeonApiKey, installNeonMcpServer, neonMcpUrl, parseMcpCategories, trackedProjectMcpConfig } from "../mcp/install.js";
|
|
9
10
|
import { mintMcpApiKey, mintedKeyRevokeCommand, withdrawMintedKey } from "../mcp/mint.js";
|
|
10
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
11
11
|
import { mcpInstallableAgents, resolveInstallTargets } from "../mcp/targets.js";
|
|
12
12
|
import { confirmMcpInstall } from "../mcp/wizard.js";
|
|
13
13
|
import { resolveMcpPlan } from "../mcp/plan.js";
|
package/dist/commands/plugins.js
CHANGED
|
@@ -2,14 +2,14 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { log } from "../log.js";
|
|
3
3
|
import { writer } from "../writer.js";
|
|
4
4
|
import { noPassthrough } from "../utils/flags.js";
|
|
5
|
-
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
6
|
-
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
7
5
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
8
6
|
import "../init/agents.js";
|
|
9
|
-
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
10
7
|
import { pluginsInstallableAgents } from "../plugins/targets.js";
|
|
11
|
-
import {
|
|
8
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
12
9
|
import { NEON_PLUGIN_NAME, PLUGIN_SKILLS, PLUGIN_SOURCE, neonPluginsRetryCommand, pluginsAddArgs, runPluginsCli } from "../plugins/run.js";
|
|
10
|
+
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
11
|
+
import { NEON_MCP_URL } from "../mcp/install.js";
|
|
12
|
+
import { resolvePluginsPlan } from "../plugins/plan.js";
|
|
13
13
|
import { confirmPluginsInstall } from "../plugins/wizard.js";
|
|
14
14
|
//#region src/commands/plugins.ts
|
|
15
15
|
var plugins_exports = /* @__PURE__ */ __exportAll({
|
package/dist/commands/profile.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { t as writeSecretFile } from "../_chunks/secure_file-BucZj4yQ.js";
|
|
3
|
-
import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-
|
|
3
|
+
import { c as describeScope, d as scopeOf, l as interpretCredentials, n as KEYRING_SERVICE, o as apiKeyCredentials, r as keyringAccount, s as credentialLabel, t as storeFor } from "../_chunks/credential_io-YeAxg9Xn.js";
|
|
4
4
|
import { isCi } from "../env.js";
|
|
5
5
|
import { log } from "../log.js";
|
|
6
6
|
import { getApiClient, isNeonApiError } from "../api.js";
|
|
7
|
-
import {
|
|
7
|
+
import { a as isOwnedCredentialPath, n as credentialsPath } from "../_chunks/paths-DMq0Lt7a.js";
|
|
8
8
|
import { setAuthContext } from "../auth_context.js";
|
|
9
|
-
import { _ as upsertProfile, a as canDropProfilesFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-
|
|
9
|
+
import { _ as upsertProfile, a as canDropProfilesFile, c as listProfiles, d as newProfileLocation, f as profilesFilePath, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, o as credentialsDisplay, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, u as locationOf, v as CRED_STORAGE_FILE, y as CRED_STORAGE_KEYRING } from "../_chunks/profiles-CvnFEQyd.js";
|
|
10
10
|
import { auth, revokeToken } from "../auth.js";
|
|
11
|
-
import { t as credentialInputs } from "../_chunks/auth_selection-
|
|
11
|
+
import { t as credentialInputs } from "../_chunks/auth_selection-pitMFzDe.js";
|
|
12
12
|
import { readOutgoingCredential, retirePreviousCredential, revokeTokenSet, withdrawKey } from "../retire_credential.js";
|
|
13
13
|
import { authFlow, usableCredential } from "./auth.js";
|
|
14
14
|
import { writer } from "../writer.js";
|
package/dist/commands/skills.js
CHANGED
|
@@ -2,10 +2,10 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
|
2
2
|
import { log } from "../log.js";
|
|
3
3
|
import { writer } from "../writer.js";
|
|
4
4
|
import { noPassthrough } from "../utils/flags.js";
|
|
5
|
-
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
6
5
|
import { getAgentDisplayName } from "../mcp/agents.js";
|
|
7
6
|
import "../init/agents.js";
|
|
8
7
|
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
8
|
+
import { helpCsv, helpEpilogue } from "../utils/help_text.js";
|
|
9
9
|
import { skillsHelpValues, skillsYesHelp } from "../skills/catalog.js";
|
|
10
10
|
import { mappedSkillsAgentNames, skillsInstallableAgents } from "../skills/targets.js";
|
|
11
11
|
import { confirmSkillsInstall, confirmSkillsUpdate } from "../skills/wizard.js";
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isCi } from "./env.js";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as isOwnedCredentialPath, i as isInsideConfigDir, n as credentialsPath, r as defaultDir, t as CREDENTIALS_FILE } from "./_chunks/paths-DMq0Lt7a.js";
|
|
3
3
|
import { existsSync, mkdirSync } from "node:fs";
|
|
4
4
|
//#region src/config.ts
|
|
5
5
|
const ensureConfigDir = ({ "config-dir": configDirArg, "force-auth": forceAuth }) => {
|
package/dist/credential_io.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as storeFor } from "./_chunks/credential_io-
|
|
1
|
+
import { t as storeFor } from "./_chunks/credential_io-YeAxg9Xn.js";
|
|
2
2
|
export { storeFor };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { log } from "./log.js";
|
|
2
2
|
import pkg_default from "./pkg.js";
|
|
3
3
|
import { isNeonApiError, messageFromBody } from "./api.js";
|
|
4
|
-
import {
|
|
4
|
+
import { r as defaultDir } from "./_chunks/paths-DMq0Lt7a.js";
|
|
5
5
|
import { ensureConfigDir } from "./config.js";
|
|
6
6
|
import { authFailureMessage, credentialsToClearOn401, getAuthContext } from "./auth_context.js";
|
|
7
7
|
import { currentContextFile, enrichFromContext } from "./context.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
//#region src/init/child.ts
|
|
3
|
+
const AUTH_CHILD = /* @__PURE__ */ new Set([
|
|
4
|
+
"bootstrap",
|
|
5
|
+
"link",
|
|
6
|
+
"mcp"
|
|
7
|
+
]);
|
|
8
|
+
const initChildEnv = (command, overlay, base = process.env) => {
|
|
9
|
+
const child = { ...base };
|
|
10
|
+
if (command === void 0 || !AUTH_CHILD.has(command)) {
|
|
11
|
+
for (const key of Object.keys(child)) if (key.toUpperCase() === "NEON_API_KEY") delete child[key];
|
|
12
|
+
}
|
|
13
|
+
return overlay ? {
|
|
14
|
+
...child,
|
|
15
|
+
...overlay
|
|
16
|
+
} : child;
|
|
17
|
+
};
|
|
18
|
+
const spawnCliChild = async (argv, cwd, overlay) => {
|
|
19
|
+
const cli = process.argv[1];
|
|
20
|
+
if (!cli) throw new Error("Cannot re-exec the Neon CLI: process.argv[1] is missing.");
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const child = spawn(process.execPath, [cli, ...argv], {
|
|
23
|
+
cwd,
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
env: initChildEnv(argv[0], overlay)
|
|
26
|
+
});
|
|
27
|
+
child.on("error", () => {
|
|
28
|
+
resolve(false);
|
|
29
|
+
});
|
|
30
|
+
child.on("close", (code) => {
|
|
31
|
+
resolve(code === 0);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { AUTH_CHILD, initChildEnv, spawnCliChild };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
|
+
//#region src/init/plan.ts
|
|
3
|
+
const directoryIsEmpty = (names) => names.filter((name) => name !== ".git").length === 0;
|
|
4
|
+
const bootstrapInitStep = (yes) => yes ? [
|
|
5
|
+
"bootstrap",
|
|
6
|
+
".",
|
|
7
|
+
"--default"
|
|
8
|
+
] : ["bootstrap", "."];
|
|
9
|
+
const projectContextFile = (projectDir, contextFile) => {
|
|
10
|
+
const resolved = isAbsolute(contextFile) ? contextFile : resolve(projectDir, contextFile);
|
|
11
|
+
const rel = relative(projectDir, resolved);
|
|
12
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return join(projectDir, ".neon");
|
|
13
|
+
return resolved;
|
|
14
|
+
};
|
|
15
|
+
const planAgentSteps = (input) => {
|
|
16
|
+
const y = input.yes ? ["-y"] : [];
|
|
17
|
+
if (input.agentSetup === "plugin") return [["plugins", ...y]];
|
|
18
|
+
if (input.agentSetup === "skills-mcp") return [["skills", ...y], ["mcp", ...y]];
|
|
19
|
+
return [];
|
|
20
|
+
};
|
|
21
|
+
const planExistingInit = (input) => {
|
|
22
|
+
const steps = [...planAgentSteps(input)];
|
|
23
|
+
if (!input.linked) steps.push(input.yes ? ["link", "--yes"] : ["link"]);
|
|
24
|
+
steps.push(input.yes ? [
|
|
25
|
+
"config",
|
|
26
|
+
"init",
|
|
27
|
+
"--services",
|
|
28
|
+
"none"
|
|
29
|
+
] : ["config", "init"]);
|
|
30
|
+
return steps;
|
|
31
|
+
};
|
|
32
|
+
const resolveInitAgentSetup = async (input) => {
|
|
33
|
+
if (input.yes) return input.hasProjectPlugins ? "plugin" : "skills-mcp";
|
|
34
|
+
if (input.interactive) return input.pick();
|
|
35
|
+
return "skills-mcp";
|
|
36
|
+
};
|
|
37
|
+
const childArgv = (step, forward) => {
|
|
38
|
+
const args = [...step];
|
|
39
|
+
if (forward.configDir) args.push("--config-dir", forward.configDir);
|
|
40
|
+
if (forward.profile) args.push("--profile", forward.profile);
|
|
41
|
+
args.push("--api-host", forward.apiHost);
|
|
42
|
+
args.push("--context-file", forward.contextFile);
|
|
43
|
+
if (forward.analytics === false) args.push("--no-analytics");
|
|
44
|
+
return args;
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { bootstrapInitStep, childArgv, directoryIsEmpty, planAgentSteps, planExistingInit, projectContextFile, resolveInitAgentSetup };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { log } from "../log.js";
|
|
2
|
+
import { getCliName } from "../utils/cli_name.js";
|
|
3
|
+
import { AUTH_CHILD } from "./child.js";
|
|
4
|
+
import { childArgv, planAgentSteps, resolveInitAgentSetup } from "./plan.js";
|
|
5
|
+
import { detectInstallablePluginsAgents } from "../plugins/targets.js";
|
|
6
|
+
import { canPickAgentsInteractively } from "../utils/agent_picker.js";
|
|
7
|
+
import { pickAgentSetupInteractively } from "./wizard.js";
|
|
8
|
+
//#region src/init/tooling.ts
|
|
9
|
+
const defaultHasProjectPlugins = async (cwd) => {
|
|
10
|
+
return (await detectInstallablePluginsAgents({
|
|
11
|
+
scope: "project",
|
|
12
|
+
cwd
|
|
13
|
+
})).length > 0;
|
|
14
|
+
};
|
|
15
|
+
const runInitSteps = async (steps, options) => {
|
|
16
|
+
for (const step of steps) {
|
|
17
|
+
log.info("Running `%s %s`", getCliName(), step.join(" "));
|
|
18
|
+
const argv = childArgv(step, options.forward);
|
|
19
|
+
const command = step[0];
|
|
20
|
+
if (!await options.run(argv, options.cwd, command !== void 0 && AUTH_CHILD.has(command) ? options.authEnv : void 0)) throw new Error(`\`${getCliName()} ${step.join(" ")}\` failed.`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const runAgentTooling = async (options) => {
|
|
24
|
+
const yes = options.yes;
|
|
25
|
+
const hasProjectPlugins = yes ? await (options.hasProjectPlugins ?? defaultHasProjectPlugins)(options.cwd) : false;
|
|
26
|
+
const interactive = !yes && (options.pickAgentSetup !== void 0 || canPickAgentsInteractively());
|
|
27
|
+
const agentSetup = await resolveInitAgentSetup({
|
|
28
|
+
yes,
|
|
29
|
+
interactive,
|
|
30
|
+
hasProjectPlugins,
|
|
31
|
+
pick: options.pickAgentSetup ?? pickAgentSetupInteractively
|
|
32
|
+
});
|
|
33
|
+
await runInitSteps(planAgentSteps({
|
|
34
|
+
yes,
|
|
35
|
+
agentSetup
|
|
36
|
+
}), options);
|
|
37
|
+
};
|
|
38
|
+
const runScaffoldFollowUp = async (options) => {
|
|
39
|
+
if (!options.skipAgentSetup) await runAgentTooling(options);
|
|
40
|
+
if (options.shouldLink) await runInitSteps([options.linkYes ? ["link", "--yes"] : ["link"]], options);
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { runAgentTooling, runInitSteps, runScaffoldFollowUp };
|