neon 4.11.1 → 4.13.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 +14 -2
- package/dist/analytics.js +6 -1
- package/dist/commands/ask.js +152 -0
- package/dist/commands/ask_sse.js +101 -0
- package/dist/commands/auth.js +2 -1
- package/dist/commands/config.js +7 -10
- package/dist/commands/dev.js +61 -19
- package/dist/commands/functions.js +33 -23
- package/dist/commands/index.js +2 -0
- package/dist/commands/thinking_spinner.js +47 -0
- package/dist/context.js +3 -1
- package/dist/dev/functions.js +2 -1
- package/dist/index.js +1 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -675,7 +675,7 @@ neon config plan --project-id polished-snowflake-12345678 --output json
|
|
|
675
675
|
neon deploy --branch my-feature --update-existing
|
|
676
676
|
```
|
|
677
677
|
|
|
678
|
-
Function deploys declared under `preview.functions` are bundled by
|
|
678
|
+
Function deploys declared under `preview.functions` are bundled with esbuild by default. A directory `source` is discovered as `index.ts`, then `index.js`, then `index.mjs`. Set `bundler: "none"` to ship a prebuilt directory as-is. `neon function deploy --no-bundle` is the same switch without a `neon.ts`.
|
|
679
679
|
|
|
680
680
|
When a package cannot be bundled — a native addon with no esbuild loader, or an optional peer dependency a library references on an untaken code path — list it in that function's `externalPackages` and the bundler leaves the import alone. `neon dev` honours the same list. It does not make the package resolvable in the deployed archive (there is no `node_modules` next to the bundle), so it only unblocks an import that is never evaluated — a dependency the handler actually calls has to be bundled, and a natively-backed one cannot be. See [`@neon/config`](../config/README.md#unbundleable-dependencies-externalpackages).
|
|
681
681
|
|
|
@@ -806,6 +806,17 @@ On a TTY the command asks which agents and which skills, then shows a summary to
|
|
|
806
806
|
|
|
807
807
|
Supported agents match `neon mcp`, minus agents that cannot install skills: `antigravity`, `cline`, `cline-cli`, `claude-code`, `claude-desktop`, `codex`, `cursor`, `gemini-cli`, `goose`, `github-copilot-cli`, `grok-build`, `opencode`, `vscode`, `windsurf`, `zed`. `mcporter` is a known MCP name that is then skipped. `neon skills --help` lists the same skill and agent values, and that `-y` leaves out `neon-postgres-agent-platforms`.
|
|
808
808
|
|
|
809
|
+
## Ask the Neon assistant (`ask`)
|
|
810
|
+
|
|
811
|
+
`neon ask --prompt` asks the hosted Neon assistant a question about Neon. It does not log in and does not use your Neon account.
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
neon ask --prompt "How do schema-only branches work?"
|
|
815
|
+
neon ask --prompt "How do schema-only branches work?" --output json
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
Default table output is the assistant's text. On a TTY that is a spinner, then the streamed reply. `--output json` and `--output yaml` print `{ "text": "…" }` after the full response.
|
|
819
|
+
|
|
809
820
|
## Install the Neon plugin (`plugins`)
|
|
810
821
|
|
|
811
822
|
`neon plugins` installs the Neon agent plugin (`neon-postgres`) by running `npx plugins add`. It does not call the Neon API.
|
|
@@ -1234,6 +1245,7 @@ Id Name Project Created At Last Used At Last
|
|
|
1234
1245
|
| mcp | | Install the Neon MCP server |
|
|
1235
1246
|
| plugins | | Install the Neon plugin |
|
|
1236
1247
|
| skills | `update` | Install Neon agent skills |
|
|
1248
|
+
| ask | | Ask a question about Neon |
|
|
1237
1249
|
| bucket | `create`, `list`, `delete`, `object list`, `object get`, `object put`, `object delete` (incl. `--recursive`) | Manage buckets and their objects |
|
|
1238
1250
|
| [completion](https://neon.com/docs/reference/cli-completion) | | Generate a completion script |
|
|
1239
1251
|
|
|
@@ -1292,7 +1304,7 @@ Global options are supported with any Neon CLI command.
|
|
|
1292
1304
|
|
|
1293
1305
|
- <a id="analytics"></a>`--analytics`
|
|
1294
1306
|
|
|
1295
|
-
Analytics are enabled by default to gather information about the CLI commands and options that are used by our customers. This data collection assists in offering support, and allows for a better understanding of typical usage patterns so that we can improve user experience. Neon does not collect user-defined data, such as project IDs or command payloads
|
|
1307
|
+
Analytics are enabled by default to gather information about the CLI commands and options that are used by our customers. This data collection assists in offering support, and allows for a better understanding of typical usage patterns so that we can improve user experience. Neon does not collect user-defined data, such as project IDs or command payloads, except the question passed to `neon ask --prompt`. To opt-out of analytics data collection, specify `--no-analytics` or `--analytics false`.
|
|
1296
1308
|
|
|
1297
1309
|
- <a id="version"></a>`-v, --version`
|
|
1298
1310
|
|
package/dist/analytics.js
CHANGED
|
@@ -193,9 +193,14 @@ const getErrorAnalyticsEventContext = (_args) => ({
|
|
|
193
193
|
ci: isCi(),
|
|
194
194
|
agent: getCliAgent(process.env)
|
|
195
195
|
});
|
|
196
|
+
const analyticsCommand = (args) => {
|
|
197
|
+
const command = args._.join(" ");
|
|
198
|
+
if (args._[0] !== "ask" || typeof args.prompt !== "string") return command;
|
|
199
|
+
return `${command} ${args.prompt}`;
|
|
200
|
+
};
|
|
196
201
|
const getAnalyticsEventProperties = (args) => ({
|
|
197
202
|
version: pkg_default.version,
|
|
198
|
-
command: args
|
|
203
|
+
command: analyticsCommand(args),
|
|
199
204
|
flags: { output: args.output },
|
|
200
205
|
ci: isCi(),
|
|
201
206
|
agent: getCliAgent(process.env),
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
|
|
2
|
+
import { isCi } from "../env.js";
|
|
3
|
+
import { isNetworkError } from "../errors.js";
|
|
4
|
+
import { writer } from "../writer.js";
|
|
5
|
+
import { noPassthrough, single } from "../utils/flags.js";
|
|
6
|
+
import { isEventStreamContentType, readAskSse } from "./ask_sse.js";
|
|
7
|
+
import { createThinkingSpinner } from "./thinking_spinner.js";
|
|
8
|
+
//#region src/commands/ask.ts
|
|
9
|
+
var ask_exports = /* @__PURE__ */ __exportAll({
|
|
10
|
+
DEFAULT_ASK_URL: () => DEFAULT_ASK_URL,
|
|
11
|
+
builder: () => builder,
|
|
12
|
+
command: () => "ask",
|
|
13
|
+
describe: () => describe,
|
|
14
|
+
handler: () => handler,
|
|
15
|
+
resolveAskUrl: () => resolveAskUrl
|
|
16
|
+
});
|
|
17
|
+
const DEFAULT_ASK_URL = "https://br-frosty-cell-a5smzg39-assistant.compute.c-1.us-east-2.aws.neon.tech/ask";
|
|
18
|
+
const ASK_TIMEOUT_MS = 12e4;
|
|
19
|
+
const command = "ask";
|
|
20
|
+
const describe = "Ask a question about Neon";
|
|
21
|
+
const builder = (argv) => argv.usage("$0 ask --prompt <question>").option("prompt", {
|
|
22
|
+
describe: "The question to ask",
|
|
23
|
+
type: "string",
|
|
24
|
+
demandOption: true,
|
|
25
|
+
coerce: single("prompt", { required: true })
|
|
26
|
+
}).option("url", {
|
|
27
|
+
describe: "Override the assistant URL",
|
|
28
|
+
type: "string",
|
|
29
|
+
hidden: true,
|
|
30
|
+
coerce: single("url")
|
|
31
|
+
}).strict().check(noPassthrough("ask")).example("$0 ask --prompt \"How do schema-only branches work?\"", describe);
|
|
32
|
+
function resolveAskUrl(opts) {
|
|
33
|
+
const fromFlag = opts.url?.trim();
|
|
34
|
+
if (fromFlag) return fromFlag;
|
|
35
|
+
const fromEnv = opts.envUrl?.trim();
|
|
36
|
+
if (fromEnv) return fromEnv;
|
|
37
|
+
return DEFAULT_ASK_URL;
|
|
38
|
+
}
|
|
39
|
+
function isMachineOutput(output) {
|
|
40
|
+
return output === "json" || output === "yaml";
|
|
41
|
+
}
|
|
42
|
+
const handler = async (props) => {
|
|
43
|
+
const url = resolveAskUrl({
|
|
44
|
+
url: props.url,
|
|
45
|
+
envUrl: process.env.NEON_ASK_URL
|
|
46
|
+
});
|
|
47
|
+
const human = !isMachineOutput(props.output);
|
|
48
|
+
const spinner = createThinkingSpinner({
|
|
49
|
+
out: process.stderr,
|
|
50
|
+
isTty: Boolean(process.stderr.isTTY) && !isCi() && human
|
|
51
|
+
});
|
|
52
|
+
let streamed = "";
|
|
53
|
+
try {
|
|
54
|
+
spinner.start();
|
|
55
|
+
const text = await askAssistant({
|
|
56
|
+
prompt: props.prompt,
|
|
57
|
+
url,
|
|
58
|
+
acceptEventStream: human,
|
|
59
|
+
onStatus: (message) => {
|
|
60
|
+
spinner.setMessage(message);
|
|
61
|
+
},
|
|
62
|
+
onText: (chunk) => {
|
|
63
|
+
if (!human) return;
|
|
64
|
+
spinner.stop();
|
|
65
|
+
streamed += chunk;
|
|
66
|
+
writer(props).text(chunk);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
spinner.stop();
|
|
70
|
+
if (!human) {
|
|
71
|
+
writer(props).end({ text }, { fields: ["text"] });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (streamed === "") {
|
|
75
|
+
writer(props).text(`${text}\n`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!streamed.endsWith("\n")) writer(props).text("\n");
|
|
79
|
+
} catch (error) {
|
|
80
|
+
spinner.stop();
|
|
81
|
+
if (human && streamed !== "" && !streamed.endsWith("\n")) writer(props).text("\n");
|
|
82
|
+
throw error;
|
|
83
|
+
} finally {
|
|
84
|
+
spinner.stop();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function isAskTimeout(error) {
|
|
88
|
+
return error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
|
|
89
|
+
}
|
|
90
|
+
function askErrorMessage(body, status) {
|
|
91
|
+
if (typeof body === "object" && body !== null && "error" in body && typeof body.error === "string" && body.error.trim() !== "") return body.error;
|
|
92
|
+
return `The Neon assistant returned ${status}.`;
|
|
93
|
+
}
|
|
94
|
+
function askText(body) {
|
|
95
|
+
if (typeof body === "object" && body !== null && "text" in body && typeof body.text === "string") return body.text;
|
|
96
|
+
throw new Error("The Neon assistant returned an unexpected response.");
|
|
97
|
+
}
|
|
98
|
+
async function readJsonBody(response) {
|
|
99
|
+
const raw = await response.text();
|
|
100
|
+
if (raw.trim() === "") return;
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(raw);
|
|
103
|
+
} catch {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function readStreamedAsk(body, opts) {
|
|
108
|
+
let text = "";
|
|
109
|
+
let sawDone = false;
|
|
110
|
+
for await (const event of readAskSse(body)) switch (event.type) {
|
|
111
|
+
case "status":
|
|
112
|
+
opts.onStatus?.(event.message);
|
|
113
|
+
break;
|
|
114
|
+
case "text":
|
|
115
|
+
text += event.text;
|
|
116
|
+
opts.onText?.(event.text);
|
|
117
|
+
break;
|
|
118
|
+
case "error": throw new Error(event.error);
|
|
119
|
+
case "done": sawDone = true;
|
|
120
|
+
}
|
|
121
|
+
if (!sawDone) throw new Error("The Neon assistant returned an unexpected response.");
|
|
122
|
+
return text;
|
|
123
|
+
}
|
|
124
|
+
async function askAssistant(opts) {
|
|
125
|
+
let response;
|
|
126
|
+
try {
|
|
127
|
+
response = await fetch(opts.url, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: {
|
|
130
|
+
"Content-Type": "application/json",
|
|
131
|
+
Accept: opts.acceptEventStream ? "text/event-stream" : "application/json"
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify({ prompt: opts.prompt }),
|
|
134
|
+
signal: AbortSignal.timeout(ASK_TIMEOUT_MS)
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (isAskTimeout(error)) throw new Error("The Neon assistant did not respond in time.");
|
|
138
|
+
if (isNetworkError(error)) throw new Error("Could not reach the Neon assistant. Check your internet connection and try again.");
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const body = await readJsonBody(response);
|
|
143
|
+
throw new Error(askErrorMessage(body, response.status));
|
|
144
|
+
}
|
|
145
|
+
if (opts.acceptEventStream && isEventStreamContentType(response.headers.get("content-type") ?? void 0)) {
|
|
146
|
+
if (!response.body) throw new Error("The Neon assistant returned an unexpected response.");
|
|
147
|
+
return readStreamedAsk(response.body, opts);
|
|
148
|
+
}
|
|
149
|
+
return askText(await readJsonBody(response));
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { DEFAULT_ASK_URL, builder, command, describe, handler, resolveAskUrl, ask_exports as t };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//#region src/commands/ask_sse.ts
|
|
2
|
+
function isEventStreamContentType(header) {
|
|
3
|
+
return header?.split(";")[0]?.trim().toLowerCase() === "text/event-stream";
|
|
4
|
+
}
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null;
|
|
7
|
+
}
|
|
8
|
+
function parseSseBlock(block) {
|
|
9
|
+
let event = "message";
|
|
10
|
+
const dataLines = [];
|
|
11
|
+
for (const line of block.split(/\r?\n/)) {
|
|
12
|
+
if (line === "" || line.startsWith(":")) continue;
|
|
13
|
+
if (line.startsWith("event:")) {
|
|
14
|
+
event = line.slice(6).trim();
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (line.startsWith("data:")) dataLines.push(line.slice(5).replace(/^ /, ""));
|
|
18
|
+
}
|
|
19
|
+
if (event === "ping") return;
|
|
20
|
+
if (event === "message" && dataLines.length === 0) return;
|
|
21
|
+
return {
|
|
22
|
+
event,
|
|
23
|
+
data: dataLines.join("\n")
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function takeSseBlocks(pending) {
|
|
27
|
+
const events = [];
|
|
28
|
+
let rest = pending;
|
|
29
|
+
while (true) {
|
|
30
|
+
const lf = rest.indexOf("\n\n");
|
|
31
|
+
const crlf = rest.indexOf("\r\n\r\n");
|
|
32
|
+
let at = -1;
|
|
33
|
+
let sep = 0;
|
|
34
|
+
if (lf === -1 && crlf === -1) break;
|
|
35
|
+
if (crlf !== -1 && (lf === -1 || crlf < lf)) {
|
|
36
|
+
at = crlf;
|
|
37
|
+
sep = 4;
|
|
38
|
+
} else {
|
|
39
|
+
at = lf;
|
|
40
|
+
sep = 2;
|
|
41
|
+
}
|
|
42
|
+
const parsed = parseSseBlock(rest.slice(0, at));
|
|
43
|
+
rest = rest.slice(at + sep);
|
|
44
|
+
if (parsed) events.push(parsed);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
events,
|
|
48
|
+
rest
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function mapAskEvent(raw) {
|
|
52
|
+
if (raw.event === "done") return { type: "done" };
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = raw.data === "" ? void 0 : JSON.parse(raw.data);
|
|
56
|
+
} catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (raw.event === "status") {
|
|
60
|
+
if (!isRecord(parsed) || typeof parsed.message !== "string" || parsed.message === "") return;
|
|
61
|
+
return {
|
|
62
|
+
type: "status",
|
|
63
|
+
message: parsed.message
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (raw.event === "text") {
|
|
67
|
+
if (!isRecord(parsed) || typeof parsed.text !== "string" || parsed.text === "") return;
|
|
68
|
+
return {
|
|
69
|
+
type: "text",
|
|
70
|
+
text: parsed.text
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (raw.event === "error") return {
|
|
74
|
+
type: "error",
|
|
75
|
+
error: isRecord(parsed) && typeof parsed.error === "string" && parsed.error.trim() !== "" ? parsed.error : "The assistant failed."
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function* readAskSse(body) {
|
|
79
|
+
const decoder = new TextDecoder();
|
|
80
|
+
const reader = body.getReader();
|
|
81
|
+
let pending = "";
|
|
82
|
+
try {
|
|
83
|
+
while (true) {
|
|
84
|
+
const { done, value } = await reader.read();
|
|
85
|
+
pending += decoder.decode(value ?? /* @__PURE__ */ new Uint8Array(), { stream: !done });
|
|
86
|
+
const taken = takeSseBlocks(pending);
|
|
87
|
+
pending = taken.rest;
|
|
88
|
+
for (const raw of taken.events) {
|
|
89
|
+
const event = mapAskEvent(raw);
|
|
90
|
+
if (!event) continue;
|
|
91
|
+
yield event;
|
|
92
|
+
if (event.type === "error" || event.type === "done") return;
|
|
93
|
+
}
|
|
94
|
+
if (done) break;
|
|
95
|
+
}
|
|
96
|
+
} finally {
|
|
97
|
+
reader.releaseLock();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { isEventStreamContentType, readAskSse };
|
package/dist/commands/auth.js
CHANGED
|
@@ -5,7 +5,7 @@ import { log } from "../log.js";
|
|
|
5
5
|
import { getApiClient } from "../api.js";
|
|
6
6
|
import { a as isOwnedCredentialPath } from "../_chunks/paths-DMq0Lt7a.js";
|
|
7
7
|
import { setAuthContext } from "../auth_context.js";
|
|
8
|
-
import { currentContextFile, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile } from "../context.js";
|
|
8
|
+
import { currentContextFile, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile } from "../context.js";
|
|
9
9
|
import { _ as upsertProfile, d as newProfileLocation, g as selectProfileName, h as resolveProfile, i as assertValidProfileName, l as locationForName, m as readProfiles, n as KEYRING_CREDENTIALS, p as profilesUsingPath, r as assertProfilesUsable, s as isKeyringPointer, t as DEFAULT_PROFILE, u as locationOf } from "../_chunks/profiles-CvnFEQyd.js";
|
|
10
10
|
import { extendTokenSet } from "../utils/auth.js";
|
|
11
11
|
import { auth, refreshToken } from "../auth.js";
|
|
@@ -220,6 +220,7 @@ const ensureAuth = async (props) => {
|
|
|
220
220
|
if (isMcpOauth(props)) return;
|
|
221
221
|
if (props._[0] === "open") return;
|
|
222
222
|
if (isSkillsCommand(props) || isPluginsCommand(props)) return;
|
|
223
|
+
if (isAskCommand(props)) return;
|
|
223
224
|
if (props._[0] === "init") {
|
|
224
225
|
selectCredential({
|
|
225
226
|
...credentialInputs(),
|
package/dist/commands/config.js
CHANGED
|
@@ -23,7 +23,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
23
23
|
import { dirname, join } from "node:path";
|
|
24
24
|
import chalk from "chalk";
|
|
25
25
|
import { packagesToStage, resolveConfig } from "@neon/config";
|
|
26
|
-
import { PushConflictError, apply, assertZipWithinLimits, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, loadConfigFromFile as loadConfigFromFile$1, plan, traceNativePackages } from "@neon/config-runtime";
|
|
26
|
+
import { PushConflictError, apply, assertZipWithinLimits, bundleAsIs, createBranch, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, loadConfigFromFile as loadConfigFromFile$1, plan, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle } from "@neon/config-runtime";
|
|
27
27
|
//#region src/commands/config.ts
|
|
28
28
|
var config_exports = /* @__PURE__ */ __exportAll({
|
|
29
29
|
applyCmd: () => applyCmd,
|
|
@@ -41,27 +41,24 @@ var config_exports = /* @__PURE__ */ __exportAll({
|
|
|
41
41
|
planCmd: () => planCmd,
|
|
42
42
|
status: () => status
|
|
43
43
|
});
|
|
44
|
-
/**
|
|
45
|
-
* Bundle a function with neonctl's OWN bundler (the shared esbuild helper) so the
|
|
46
|
-
* config-runtime never has to import esbuild itself. Injecting this keeps esbuild
|
|
47
|
-
* out of config-runtime's static module graph — and therefore out of the packaged
|
|
48
|
-
* neonctl snapshot, which resolves esbuild dynamically at deploy time.
|
|
49
|
-
*/
|
|
50
44
|
const neonctlBundler = async (fn) => {
|
|
45
|
+
if (typeof fn.bundler === "function") return zipFunctionBundle(fn.slug, await fn.bundler(fn));
|
|
46
|
+
if (fn.bundler === "none") return zipFunctionBundle(fn.slug, await bundleAsIs(fn));
|
|
47
|
+
const entry = await resolveEsbuildEntry(fn.source);
|
|
51
48
|
const externalPackages = fn.externalPackages ?? [];
|
|
52
|
-
const { files, metafile, warnings } = await bundleEntry(
|
|
49
|
+
const { files, metafile, warnings } = await bundleEntry(entry, { externalPackages: externalPackages.map((pkg) => pkg.name) });
|
|
53
50
|
for (const warning of warnings) log.warning(warning);
|
|
54
51
|
for (const finding of findUndeclaredNativePackages({
|
|
55
52
|
metafile,
|
|
56
53
|
declared: externalPackages.map((pkg) => pkg.name),
|
|
57
|
-
projectDir: dirname(
|
|
54
|
+
projectDir: dirname(entry)
|
|
58
55
|
})) log.warning(describeNativeFinding(fn.slug, finding));
|
|
59
56
|
const staged = packagesToStage(externalPackages);
|
|
60
57
|
if (staged.length === 0) return zipBundle(files);
|
|
61
58
|
const traced = await traceNativePackages({
|
|
62
59
|
slug: fn.slug,
|
|
63
60
|
packages: staged,
|
|
64
|
-
projectDir: dirname(
|
|
61
|
+
projectDir: dirname(entry)
|
|
65
62
|
});
|
|
66
63
|
for (const warning of traced.warnings) log.warning(warning);
|
|
67
64
|
const entries = {
|
package/dist/commands/dev.js
CHANGED
|
@@ -7,12 +7,12 @@ import { bundleEntry } from "../utils/esbuild.js";
|
|
|
7
7
|
import { i as resolveDevEnv } from "../_chunks/env-NbA61JR3.js";
|
|
8
8
|
import { resolveFunctionsFromConfig } from "../dev/functions.js";
|
|
9
9
|
import { resolveWatchInputs } from "../dev/inputs.js";
|
|
10
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
10
|
+
import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import chalk from "chalk";
|
|
14
14
|
import { spawn, spawnSync } from "node:child_process";
|
|
15
|
-
import { describeNativeFinding, findUndeclaredNativePackages } from "@neon/config-runtime";
|
|
15
|
+
import { bundleAsIs, describeNativeFinding, findUndeclaredNativePackages, resolveEsbuildEntry } from "@neon/config-runtime";
|
|
16
16
|
import { once } from "node:events";
|
|
17
17
|
//#region src/commands/dev.ts
|
|
18
18
|
var dev_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -239,11 +239,13 @@ const plannedToUnit = (fn, branchEnv, searchBase) => {
|
|
|
239
239
|
fn: Object.keys(fn.env)
|
|
240
240
|
},
|
|
241
241
|
...fn.externalPackages ? { externalPackages: fn.externalPackages } : {},
|
|
242
|
+
...fn.bundler !== void 0 ? { bundler: fn.bundler } : {},
|
|
242
243
|
configKey: JSON.stringify({
|
|
243
244
|
source: fn.source,
|
|
244
245
|
port: fn.port ?? null,
|
|
245
246
|
env: fn.env,
|
|
246
|
-
externalPackages: fn.externalPackages ?? null
|
|
247
|
+
externalPackages: fn.externalPackages ?? null,
|
|
248
|
+
bundler: typeof fn.bundler === "function" ? "custom" : fn.bundler ?? null
|
|
247
249
|
})
|
|
248
250
|
};
|
|
249
251
|
};
|
|
@@ -286,7 +288,7 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
286
288
|
const bundleAndStart = async (r) => {
|
|
287
289
|
let bundlePath;
|
|
288
290
|
try {
|
|
289
|
-
bundlePath = await writeBundle(r.unit.source, r.unit.bundleDir, r.unit.externalPackages, r.unit.slug ?? void 0);
|
|
291
|
+
bundlePath = await writeBundle(r.unit.source, r.unit.bundleDir, r.unit.externalPackages, r.unit.slug ?? void 0, r.unit.bundler);
|
|
290
292
|
} catch (err) {
|
|
291
293
|
r.status = "error";
|
|
292
294
|
logUnit(r.unit, chalk.red("bundle failed: ") + (err instanceof Error ? err.message : String(err)));
|
|
@@ -329,7 +331,7 @@ const runSupervisor = async (units, options = {}) => {
|
|
|
329
331
|
}, 150);
|
|
330
332
|
};
|
|
331
333
|
const startUnit = async (r) => {
|
|
332
|
-
r.watcher = await startWatcher(r.unit
|
|
334
|
+
r.watcher = await startWatcher(r.unit, () => {
|
|
333
335
|
restart(r);
|
|
334
336
|
});
|
|
335
337
|
await bundleAndStart(r);
|
|
@@ -500,22 +502,50 @@ const spawnChild = (unit, runtimePath, bundlePath) => {
|
|
|
500
502
|
* positive would otherwise repeat the whole block for its lifetime.
|
|
501
503
|
*/
|
|
502
504
|
const reportedFindings = /* @__PURE__ */ new Map();
|
|
503
|
-
const writeBundle = async (source, bundleDir, externalPackages, label) => {
|
|
504
|
-
|
|
505
|
+
const writeBundle = async (source, bundleDir, externalPackages, label, bundler) => {
|
|
506
|
+
if (bundler !== void 0 && bundler !== "esbuild") {
|
|
507
|
+
const files = await bundleForDev(source, label, bundler);
|
|
508
|
+
return writeBundleFiles(bundleDir, files);
|
|
509
|
+
}
|
|
510
|
+
const entry = await resolveEsbuildEntry(source);
|
|
511
|
+
const { files, metafile, warnings } = await bundleEntry(entry, { ...externalPackages ? { externalPackages } : {} });
|
|
505
512
|
for (const warning of warnings) log.warning(warning);
|
|
506
513
|
const findings = findUndeclaredNativePackages({
|
|
507
514
|
metafile,
|
|
508
515
|
declared: externalPackages ?? [],
|
|
509
|
-
projectDir: dirname(
|
|
516
|
+
projectDir: dirname(entry)
|
|
510
517
|
});
|
|
511
518
|
const signature = findings.map((f) => f.name).join(",");
|
|
512
519
|
if (reportedFindings.get(source) !== signature) {
|
|
513
520
|
reportedFindings.set(source, signature);
|
|
514
521
|
for (const finding of findings) log.warning(describeNativeFinding(label ?? basename(source), finding));
|
|
515
522
|
}
|
|
523
|
+
return writeBundleFiles(bundleDir, files);
|
|
524
|
+
};
|
|
525
|
+
const bundleForDev = async (source, label, bundler) => {
|
|
526
|
+
const fn = {
|
|
527
|
+
slug: label ?? basename(source),
|
|
528
|
+
name: label ?? basename(source),
|
|
529
|
+
source,
|
|
530
|
+
env: {},
|
|
531
|
+
runtime: "nodejs24",
|
|
532
|
+
bundler
|
|
533
|
+
};
|
|
534
|
+
return bundler === "none" ? bundleAsIs(fn) : bundler(fn);
|
|
535
|
+
};
|
|
536
|
+
const writeBundleFiles = (bundleDir, files) => {
|
|
537
|
+
rmSync(bundleDir, {
|
|
538
|
+
recursive: true,
|
|
539
|
+
force: true
|
|
540
|
+
});
|
|
516
541
|
mkdirSync(bundleDir, { recursive: true });
|
|
517
|
-
for (const [name, contents] of Object.entries(files))
|
|
518
|
-
|
|
542
|
+
for (const [name, contents] of Object.entries(files)) {
|
|
543
|
+
const dest = join(bundleDir, name);
|
|
544
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
545
|
+
writeFileSync(dest, contents);
|
|
546
|
+
}
|
|
547
|
+
const entry = ["index.mjs", "index.js"].find((name) => name in files);
|
|
548
|
+
return join(bundleDir, entry ?? "index.mjs");
|
|
519
549
|
};
|
|
520
550
|
const urlFor = (port) => port === null ? chalk.red("not running") : `http://localhost:${port}`;
|
|
521
551
|
const waitForReady = (child) => new Promise((resolveReady) => {
|
|
@@ -594,11 +624,19 @@ const logUnit = (unit, message) => {
|
|
|
594
624
|
const prefix = unit.label ? chalk.dim(`[${unit.label}] `) : "";
|
|
595
625
|
log.info(`${prefix}${message}`);
|
|
596
626
|
};
|
|
597
|
-
const startWatcher = async (
|
|
627
|
+
const startWatcher = async (unit, restart) => {
|
|
598
628
|
const { default: chokidar } = await import("chokidar");
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
629
|
+
if (unit.bundler !== void 0 && unit.bundler !== "esbuild") {
|
|
630
|
+
const dir = statSync(unit.source, { throwIfNoEntry: false })?.isDirectory() === true ? unit.source : dirname(unit.source);
|
|
631
|
+
return startDirectoryWatcher(chokidar, dir, restart, { ignoreDist: false });
|
|
632
|
+
}
|
|
633
|
+
let entry = unit.source;
|
|
634
|
+
try {
|
|
635
|
+
entry = await resolveEsbuildEntry(unit.source);
|
|
636
|
+
} catch {}
|
|
637
|
+
const initialInputs = await resolveWatchInputs(entry);
|
|
638
|
+
if (initialInputs === null) return startDirectoryWatcher(chokidar, entry, restart, { ignoreDist: true });
|
|
639
|
+
return startInputWatcher(chokidar, entry, initialInputs, restart);
|
|
602
640
|
};
|
|
603
641
|
/**
|
|
604
642
|
* Watch the neon.ts file itself for changes, firing `onChange` on every save. Used by the
|
|
@@ -645,11 +683,15 @@ const startInputWatcher = async (chokidar, source, initialInputs, restart) => {
|
|
|
645
683
|
close: () => watcher.close()
|
|
646
684
|
};
|
|
647
685
|
};
|
|
648
|
-
const startDirectoryWatcher = async (chokidar, source, restart) => {
|
|
649
|
-
const watchedDir = dirname(source);
|
|
686
|
+
const startDirectoryWatcher = async (chokidar, source, restart, options) => {
|
|
687
|
+
const watchedDir = statSync(source, { throwIfNoEntry: false })?.isDirectory() ? source : dirname(source);
|
|
650
688
|
const isIgnored = (p) => {
|
|
651
|
-
const
|
|
652
|
-
|
|
689
|
+
const rel = relative(watchedDir, p);
|
|
690
|
+
if (rel === "" || rel === ".") return false;
|
|
691
|
+
if (rel.startsWith("..")) return false;
|
|
692
|
+
const segments = rel.split(/[/\\]/);
|
|
693
|
+
if (segments.includes("node_modules") || segments.includes(".git")) return true;
|
|
694
|
+
return options.ignoreDist && segments.includes("dist");
|
|
653
695
|
};
|
|
654
696
|
const watcher = chokidar.watch(watchedDir, {
|
|
655
697
|
ignoreInitial: true,
|
|
@@ -7,9 +7,10 @@ import { writer } from "../writer.js";
|
|
|
7
7
|
import { branchIdFromProps, fillSingleProject } from "../utils/enrichers.js";
|
|
8
8
|
import { bundleEntry } from "../utils/esbuild.js";
|
|
9
9
|
import { zipBundle } from "../utils/zip.js";
|
|
10
|
-
import {
|
|
11
|
-
import { dirname
|
|
12
|
-
import {
|
|
10
|
+
import { statSync } from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { FUNCTION_SOURCE_ENTRIES } from "@neon/config";
|
|
13
|
+
import { bundleAsIs, describeNativeFinding, findUndeclaredNativePackages, resolveEsbuildEntry, zipFunctionBundle } from "@neon/config-runtime";
|
|
13
14
|
//#region src/commands/functions.ts
|
|
14
15
|
var functions_exports = /* @__PURE__ */ __exportAll({
|
|
15
16
|
aliases: () => aliases,
|
|
@@ -55,11 +56,6 @@ const writeDeploymentErrorSection = (out, dep) => {
|
|
|
55
56
|
};
|
|
56
57
|
const SLUG_PATTERN = /^[a-z0-9]{1,20}$/;
|
|
57
58
|
const SLUG_HELP = "Use 1-20 lowercase letters and digits (no hyphens or other characters).";
|
|
58
|
-
const ENTRY_CANDIDATES = [
|
|
59
|
-
"index.ts",
|
|
60
|
-
"index.mjs",
|
|
61
|
-
"index.js"
|
|
62
|
-
];
|
|
63
59
|
const POLL_INTERVAL_MS = Number(process.env.NEON_FUNCTIONS_POLL_INTERVAL_MS) || 2e3;
|
|
64
60
|
const POLL_TIMEOUT_MS = Number(process.env.NEON_FUNCTIONS_POLL_TIMEOUT_MS) || 6e5;
|
|
65
61
|
const command = "functions";
|
|
@@ -80,7 +76,7 @@ const builder = (argv) => argv.usage("$0 function <sub-command> [options]").opti
|
|
|
80
76
|
demandOption: true
|
|
81
77
|
}).options({
|
|
82
78
|
src: {
|
|
83
|
-
describe: "Function source: a directory containing index.ts, index.
|
|
79
|
+
describe: "Function source: a directory containing index.ts, index.js, or index.mjs (first file match is the entry), or a path to the entry file",
|
|
84
80
|
type: "string"
|
|
85
81
|
},
|
|
86
82
|
path: {
|
|
@@ -105,6 +101,11 @@ const builder = (argv) => argv.usage("$0 function <sub-command> [options]").opti
|
|
|
105
101
|
describe: "Wait for the deployment to finish building",
|
|
106
102
|
type: "boolean",
|
|
107
103
|
default: true
|
|
104
|
+
},
|
|
105
|
+
bundle: {
|
|
106
|
+
describe: "Bundle --src with esbuild. Use --no-bundle to zip a prebuilt directory (root must contain index.mjs or index.js) or a file of that name.",
|
|
107
|
+
type: "boolean",
|
|
108
|
+
default: true
|
|
108
109
|
}
|
|
109
110
|
}), (args) => deploy(args)).command("list", "List functions on the branch", (yargs) => yargs, (args) => list(args)).command("get <slug>", "Show a function's details", (yargs) => yargs.positional("slug", {
|
|
110
111
|
describe: "Function slug",
|
|
@@ -144,24 +145,33 @@ const emitDeployResult = (props, deployment, fn) => {
|
|
|
144
145
|
};
|
|
145
146
|
const isTransient = (err) => isNeonApiError(err) && (err.status === void 0 || err.status === 404 || err.status >= 500);
|
|
146
147
|
const deploy = async (props) => {
|
|
147
|
-
if (props.path !== void 0 || props.entry !== void 0) throw new Error(
|
|
148
|
-
if (!(props.src !== void 0 || props.env !== void 0 || props.runtime !== void 0)) throw new Error(`Provide at least one option to deploy, e.g. --src or --
|
|
148
|
+
if (props.path !== void 0 || props.entry !== void 0) throw new Error(`--path and --entry were removed. Use --src <dir>; the entry point is discovered as ${FUNCTION_SOURCE_ENTRIES.join(", ")} in that directory.`);
|
|
149
|
+
if (!(props.src !== void 0 || props.env !== void 0 || props.runtime !== void 0 || props.bundle === false)) throw new Error(`Provide at least one option to deploy, e.g. --src, --env, or --no-bundle. See: ${getCliName()} function deploy --help.`);
|
|
149
150
|
if (!SLUG_PATTERN.test(props.slug)) throw new Error(`Invalid function slug "${props.slug}". ${SLUG_HELP}`);
|
|
150
151
|
const src = props.src ?? ".";
|
|
151
152
|
const runtime = props.runtime ?? "nodejs24";
|
|
152
153
|
const environment = parseEnv(props.env);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
if (statSync(src, { throwIfNoEntry: false }) === void 0) throw new Error(`--src path not found: ${src}.`);
|
|
155
|
+
let zip;
|
|
156
|
+
if (!props.bundle) zip = await zipFunctionBundle(props.slug, await bundleAsIs({
|
|
157
|
+
slug: props.slug,
|
|
158
|
+
name: props.slug,
|
|
159
|
+
source: src,
|
|
160
|
+
env: {},
|
|
161
|
+
runtime: "nodejs24",
|
|
162
|
+
bundler: "none"
|
|
163
|
+
}, { via: "no-bundle" }));
|
|
164
|
+
else {
|
|
165
|
+
const source = await resolveEsbuildEntry(src);
|
|
166
|
+
const bundled = await bundleEntry(source);
|
|
167
|
+
for (const warning of bundled.warnings) log.warning(warning);
|
|
168
|
+
for (const finding of findUndeclaredNativePackages({
|
|
169
|
+
metafile: bundled.metafile,
|
|
170
|
+
declared: [],
|
|
171
|
+
projectDir: dirname(source)
|
|
172
|
+
})) log.warning(describeNativeFinding(props.slug, finding));
|
|
173
|
+
zip = zipBundle(bundled.files);
|
|
174
|
+
}
|
|
165
175
|
const branchId = await branchIdFromProps(props);
|
|
166
176
|
let before;
|
|
167
177
|
try {
|
package/dist/commands/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { t as auth_exports } from "./auth.js";
|
|
2
2
|
import { t as api_exports } from "./api.js";
|
|
3
3
|
import { t as api_keys_exports } from "./api_keys.js";
|
|
4
|
+
import { t as ask_exports } from "./ask.js";
|
|
4
5
|
import { t as bootstrap_exports } from "./bootstrap.js";
|
|
5
6
|
import { t as branches_exports } from "./branches.js";
|
|
6
7
|
import { t as bucket_exports } from "./bucket.js";
|
|
@@ -66,6 +67,7 @@ var commands_default = [
|
|
|
66
67
|
mcp_exports,
|
|
67
68
|
plugins_exports,
|
|
68
69
|
skills_exports,
|
|
70
|
+
ask_exports,
|
|
69
71
|
data_api_exports,
|
|
70
72
|
functions_exports,
|
|
71
73
|
dev_exports,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/commands/thinking_spinner.ts
|
|
2
|
+
const FRAMES = [
|
|
3
|
+
"⠋",
|
|
4
|
+
"⠙",
|
|
5
|
+
"⠹",
|
|
6
|
+
"⠸",
|
|
7
|
+
"⠼",
|
|
8
|
+
"⠴",
|
|
9
|
+
"⠦",
|
|
10
|
+
"⠧",
|
|
11
|
+
"⠇",
|
|
12
|
+
"⠏"
|
|
13
|
+
];
|
|
14
|
+
const INTERVAL_MS = 80;
|
|
15
|
+
const CLEAR_LINE = "\r\x1B[2K";
|
|
16
|
+
function createThinkingSpinner(opts) {
|
|
17
|
+
let message = "Thinking";
|
|
18
|
+
let frame = 0;
|
|
19
|
+
let timer;
|
|
20
|
+
let stopped = false;
|
|
21
|
+
const paint = () => {
|
|
22
|
+
if (!opts.isTty || stopped) return;
|
|
23
|
+
const glyph = FRAMES[frame % FRAMES.length];
|
|
24
|
+
frame += 1;
|
|
25
|
+
opts.out.write(`${CLEAR_LINE}${glyph} ${message}`);
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
start() {
|
|
29
|
+
if (!opts.isTty || stopped) return;
|
|
30
|
+
paint();
|
|
31
|
+
timer = setInterval(paint, INTERVAL_MS);
|
|
32
|
+
timer.unref();
|
|
33
|
+
},
|
|
34
|
+
setMessage(next) {
|
|
35
|
+
message = next;
|
|
36
|
+
paint();
|
|
37
|
+
},
|
|
38
|
+
stop() {
|
|
39
|
+
if (stopped) return;
|
|
40
|
+
stopped = true;
|
|
41
|
+
if (timer) clearInterval(timer);
|
|
42
|
+
if (opts.isTty) opts.out.write(CLEAR_LINE);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { createThinkingSpinner };
|
package/dist/context.js
CHANGED
|
@@ -54,6 +54,7 @@ const isApiKeysCommand = (args) => args._[0] === "api-keys" || args._[0] === "ap
|
|
|
54
54
|
const isMcpCommand = (args) => args._[0] === "mcp";
|
|
55
55
|
const isSkillsCommand = (args) => args._[0] === "skills" || args._[0] === "skill";
|
|
56
56
|
const isPluginsCommand = (args) => args._[0] === "plugins" || args._[0] === "plugin";
|
|
57
|
+
const isAskCommand = (args) => args._[0] === "ask";
|
|
57
58
|
/** Raw argv is required because auth middleware runs before MCP flags are parsed. */
|
|
58
59
|
const isMcpOauth = (args) => isMcpCommand(args) && argvEnablesMcpOauth(process.argv);
|
|
59
60
|
const OAUTH_FALSE = /* @__PURE__ */ new Set([
|
|
@@ -134,6 +135,7 @@ const enrichFromContext = (args) => {
|
|
|
134
135
|
if (isProfileCommand(args)) return;
|
|
135
136
|
if (isMcpCommand(args)) return;
|
|
136
137
|
if (isSkillsCommand(args) || isPluginsCommand(args)) return;
|
|
138
|
+
if (isAskCommand(args)) return;
|
|
137
139
|
if (isClaimCommand(args)) return;
|
|
138
140
|
const context = readContextFile(args.contextFile);
|
|
139
141
|
if (!args.orgId) args.orgId = context.orgId;
|
|
@@ -240,4 +242,4 @@ const globToRegExp = (pattern) => {
|
|
|
240
242
|
return new RegExp(`^${source}$`);
|
|
241
243
|
};
|
|
242
244
|
//#endregion
|
|
243
|
-
export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureGitignored, isApiKeysCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
|
|
245
|
+
export { applyContext, contextBranch, currentContextFile, enrichFromContext, ensureGitignored, isApiKeysCommand, isAskCommand, isClaimCommand, isConfigInit, isCurrentBranchProbe, isMcpCommand, isMcpOauth, isPluginsCommand, isProfileCommand, isSkillsCommand, readContextFile, setContext, updateContextFile, walkContextFile };
|
package/dist/dev/functions.js
CHANGED
|
@@ -29,7 +29,8 @@ const resolveFunctionsFromConfig = async (cwd, branchName) => {
|
|
|
29
29
|
source,
|
|
30
30
|
...devPort(fn.dev) !== void 0 ? { port: devPort(fn.dev) } : {},
|
|
31
31
|
env: { ...fn.env },
|
|
32
|
-
...fn.externalPackages ? { externalPackages: fn.externalPackages.map((pkg) => pkg.name) } : {}
|
|
32
|
+
...fn.externalPackages ? { externalPackages: fn.externalPackages.map((pkg) => pkg.name) } : {},
|
|
33
|
+
...fn.bundler !== "esbuild" ? { bundler: fn.bundler } : {}
|
|
33
34
|
};
|
|
34
35
|
})
|
|
35
36
|
};
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"yargs": "17.7.2",
|
|
65
65
|
"yoctocolors": "^2.1.2",
|
|
66
|
-
"@neon/config": "1.0
|
|
67
|
-
"@neon/
|
|
68
|
-
"@neon/
|
|
66
|
+
"@neon/config": "1.1.0",
|
|
67
|
+
"@neon/sdk": "3.0.0",
|
|
68
|
+
"@neon/config-runtime": "1.1.0"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -97,8 +97,8 @@
|
|
|
97
97
|
"typescript": "^5.9.0",
|
|
98
98
|
"vitest": "^3.0.9",
|
|
99
99
|
"@neon-internals/cli-core": "0.0.0",
|
|
100
|
-
"@neon
|
|
101
|
-
"@neon/
|
|
100
|
+
"@neon/e2e-harness": "0.0.0",
|
|
101
|
+
"@neon-internals/env-core": "0.0.6"
|
|
102
102
|
},
|
|
103
103
|
"publishConfig": {
|
|
104
104
|
"access": "public",
|