@oxygen-agent/cli 1.1010.721 → 1.1010.950
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 +1 -1
- package/dist/auto-update.d.ts +129 -0
- package/dist/auto-update.js +392 -0
- package/dist/command-manifest.js +14 -0
- package/dist/contract-flags.d.ts +42 -0
- package/dist/contract-flags.js +220 -0
- package/dist/credentials.d.ts +2 -0
- package/dist/credentials.js +6 -3
- package/dist/functions-commands.js +1 -1
- package/dist/http-client.js +28 -4
- package/dist/index.js +622 -325
- package/dist/run-wait.d.ts +3 -1
- package/dist/run-wait.js +19 -5
- package/dist/sender-limits-flags.d.ts +14 -0
- package/dist/sender-limits-flags.js +157 -0
- package/dist/streamed-file-import.d.ts +58 -0
- package/dist/streamed-file-import.js +115 -0
- package/dist/ugc-commands.js +16 -2
- package/dist/update.d.ts +29 -0
- package/dist/update.js +62 -16
- package/dist/workflow-plan-limit-notices.d.ts +8 -0
- package/dist/workflow-plan-limit-notices.js +28 -0
- package/node_modules/@oxygen/cli-ugc/dist/commands.js +6 -6
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.d.ts +4 -0
- package/node_modules/@oxygen/cli-ugc/dist/field-parser.js +20 -8
- package/node_modules/@oxygen/cli-ugc/dist/index.d.ts +1 -0
- package/node_modules/@oxygen/cli-ugc/dist/index.js +1 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.d.ts +17 -0
- package/node_modules/@oxygen/shared/dist/billing-anchors.js +27 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +191 -35
- package/node_modules/@oxygen/shared/dist/billing.js +333 -42
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +55 -5
- package/node_modules/@oxygen/shared/dist/contracts/operations.d.ts +39 -0
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +11 -0
- package/node_modules/@oxygen/shared/dist/contracts/schema.d.ts +186 -0
- package/node_modules/@oxygen/shared/dist/contracts/schema.js +260 -0
- package/node_modules/@oxygen/shared/dist/contracts/sender-limits.d.ts +67 -0
- package/node_modules/@oxygen/shared/dist/contracts/sender-limits.js +88 -0
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.d.ts +2 -2
- package/node_modules/@oxygen/shared/dist/copilot-skills.generated.js +2 -2
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.d.ts +50 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate-view.js +90 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate.d.ts +167 -0
- package/node_modules/@oxygen/shared/dist/cost-estimate.js +361 -0
- package/node_modules/@oxygen/shared/dist/credit-gate.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/credit-gate.js +65 -0
- package/node_modules/@oxygen/shared/dist/email-deliverability-policy.d.ts +51 -0
- package/node_modules/@oxygen/shared/dist/email-deliverability-policy.js +101 -0
- package/node_modules/@oxygen/shared/dist/email-hard-bounce.d.ts +3 -1
- package/node_modules/@oxygen/shared/dist/email-hard-bounce.js +3 -3
- package/node_modules/@oxygen/shared/dist/error-redaction.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/error-redaction.js +1 -1
- package/node_modules/@oxygen/shared/dist/feature-gates.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/feature-gates.js +5 -0
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +13 -1
- package/node_modules/@oxygen/shared/dist/file-import.js +33 -6
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +73 -3
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +246 -24
- package/node_modules/@oxygen/shared/dist/import-limits.d.ts +25 -1
- package/node_modules/@oxygen/shared/dist/import-limits.js +35 -2
- package/node_modules/@oxygen/shared/dist/index.d.ts +2 -22
- package/node_modules/@oxygen/shared/dist/index.js +2 -42
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/linkedin-sequences.js +41 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +17 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.d.ts +41 -0
- package/node_modules/@oxygen/shared/dist/operational-telemetry.js +55 -0
- package/node_modules/@oxygen/shared/dist/plan-band.d.ts +117 -1
- package/node_modules/@oxygen/shared/dist/plan-band.js +175 -10
- package/node_modules/@oxygen/shared/dist/plan-capabilities.d.ts +77 -7
- package/node_modules/@oxygen/shared/dist/plan-capabilities.js +87 -7
- package/node_modules/@oxygen/shared/dist/plan-limits-view.d.ts +219 -0
- package/node_modules/@oxygen/shared/dist/plan-limits-view.js +330 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +204 -6
- package/node_modules/@oxygen/shared/dist/plan-limits.js +197 -15
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +80 -36
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +80 -31
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.d.ts +38 -20
- package/node_modules/@oxygen/shared/dist/pricing-snapshot.generated.js +47 -34
- package/node_modules/@oxygen/shared/dist/product-briefing-rules.js +4 -2
- package/node_modules/@oxygen/shared/dist/provider-http-error.d.ts +10 -0
- package/node_modules/@oxygen/shared/dist/provider-http-error.js +27 -0
- package/node_modules/@oxygen/shared/dist/repricing.d.ts +127 -0
- package/node_modules/@oxygen/shared/dist/repricing.js +407 -6
- package/node_modules/@oxygen/shared/dist/semver.d.ts +21 -0
- package/node_modules/@oxygen/shared/dist/semver.js +41 -0
- package/node_modules/@oxygen/shared/dist/sending-limits.d.ts +5 -7
- package/node_modules/@oxygen/shared/dist/sending-limits.js +10 -16
- package/node_modules/@oxygen/shared/dist/sending-seats.d.ts +18 -15
- package/node_modules/@oxygen/shared/dist/sending-seats.js +22 -17
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +57 -8
- package/node_modules/@oxygen/shared/dist/spend-safety.js +64 -11
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.d.ts +15 -7
- package/node_modules/@oxygen/shared/dist/stripe-price-catalog.js +18 -5
- package/node_modules/@oxygen/shared/dist/table-capacity.d.ts +35 -5
- package/node_modules/@oxygen/shared/dist/table-capacity.js +40 -8
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/telemetry-export-observer.js +13 -5
- package/node_modules/@oxygen/shared/dist/telemetry-resource.d.ts +40 -0
- package/node_modules/@oxygen/shared/dist/telemetry-resource.js +35 -0
- package/node_modules/@oxygen/shared/dist/telemetry.js +5 -0
- package/node_modules/@oxygen/shared/dist/ugc.d.ts +15 -0
- package/node_modules/@oxygen/shared/dist/ugc.js +29 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -3
- package/node_modules/@oxygen/shared/dist/version.generated.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.generated.js +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +17 -27
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/workspace-file-storage.js +5 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +3 -3
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +15 -1
- package/node_modules/@oxygen/workflows/dist/graph/types.js +15 -1
- package/node_modules/@oxygen/workflows/dist/index.d.ts +45 -0
- package/node_modules/@oxygen/workflows/dist/index.js +152 -2
- package/node_modules/@oxygen/workflows/dist/usage-estimate.d.ts +10 -1
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +33 -29
- package/package.json +1 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// The CLI half of a shared resource contract (@oxygen/shared/contracts/schema).
|
|
2
|
+
// A command's flags and the request body they build are generated from the same
|
|
3
|
+
// contract that generates the MCP input schema and validates the route, so a
|
|
4
|
+
// settable field cannot exist on one surface and not the other.
|
|
5
|
+
//
|
|
6
|
+
// Derivation (the overlay only renames, re-words, aliases or hides):
|
|
7
|
+
// scalar --<kebab-name> <value> numbers parsed and range-checked
|
|
8
|
+
// enum --<kebab-name> <value> membership checked
|
|
9
|
+
// boolean --<x> / --no-<x> tri-state: absent = not sent
|
|
10
|
+
// array of scalars --<kebab-name> <a,b,c>
|
|
11
|
+
// object, <= 6 scalar fields --<obj>-<field> per field
|
|
12
|
+
// larger object --<obj>-json <json>
|
|
13
|
+
// nullable also --clear-<x>, which sends null
|
|
14
|
+
// Every value is checked inside the action (never in a Commander parser), so a
|
|
15
|
+
// bad value comes back as the JSON error envelope, not a raw stack trace.
|
|
16
|
+
import { Option } from "commander";
|
|
17
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
18
|
+
import { unwrapNullable } from "@oxygen/shared/contracts/schema";
|
|
19
|
+
const MAX_SPLAT_FIELDS = 6;
|
|
20
|
+
function kebab(name) {
|
|
21
|
+
return name.replace(/_/g, "-");
|
|
22
|
+
}
|
|
23
|
+
function attribute(flag) {
|
|
24
|
+
return flag.replace(/-([a-z0-9])/g, (_match, char) => char.toUpperCase());
|
|
25
|
+
}
|
|
26
|
+
function isObject(property) {
|
|
27
|
+
return "type" in property && property.type === "object" && property.properties !== undefined;
|
|
28
|
+
}
|
|
29
|
+
function contractFlagSpecs(properties, overlay, parentPath = [], parentFlag = "") {
|
|
30
|
+
const specs = [];
|
|
31
|
+
for (const [name, raw] of Object.entries(properties)) {
|
|
32
|
+
const path = [...parentPath, name];
|
|
33
|
+
const entry = overlay[path.join(".")] ?? {};
|
|
34
|
+
if (entry.omit)
|
|
35
|
+
continue;
|
|
36
|
+
const { property, nullable } = unwrapNullable(raw);
|
|
37
|
+
const flag = entry.flag ?? `${parentFlag}${kebab(name)}`;
|
|
38
|
+
if (isObject(property)) {
|
|
39
|
+
const fields = Object.values(property.properties ?? {});
|
|
40
|
+
const splat = entry.flatten || (fields.length <= MAX_SPLAT_FIELDS && !fields.some((field) => isObject(unwrapNullable(field).property)));
|
|
41
|
+
if (splat)
|
|
42
|
+
specs.push(...contractFlagSpecs(property.properties ?? {}, overlay, path, entry.flatten ? "" : `${flag}-`));
|
|
43
|
+
else
|
|
44
|
+
specs.push({ kind: "json", path, flag: `${flag}-json`, entry });
|
|
45
|
+
}
|
|
46
|
+
else if ("type" in property && property.type === "boolean") {
|
|
47
|
+
specs.push(entry.pair ? { kind: "pair", path, on: entry.pair.on, off: entry.pair.off, entry } : { kind: "boolean", path, flag, entry });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
specs.push({ kind: "value", path, flag, property, entry });
|
|
51
|
+
}
|
|
52
|
+
if (nullable && !entry.noClear)
|
|
53
|
+
specs.push({ kind: "clear", path, flag: `clear-${flag}`, target: flag });
|
|
54
|
+
}
|
|
55
|
+
return specs;
|
|
56
|
+
}
|
|
57
|
+
function helpFor(spec, property) {
|
|
58
|
+
if (spec.entry.help !== undefined)
|
|
59
|
+
return spec.entry.help;
|
|
60
|
+
const description = property && "description" in property ? property.description ?? "" : "";
|
|
61
|
+
const values = property && "enum" in property && property.enum ? ` One of: ${property.enum.join(", ")}.` : "";
|
|
62
|
+
return `${description}${values}`.trim();
|
|
63
|
+
}
|
|
64
|
+
function propertyAt(contract, path) {
|
|
65
|
+
let current = contract;
|
|
66
|
+
for (const key of path) {
|
|
67
|
+
const unwrapped = current ? unwrapNullable(current).property : undefined;
|
|
68
|
+
current = unwrapped && isObject(unwrapped) ? unwrapped.properties?.[key] : undefined;
|
|
69
|
+
}
|
|
70
|
+
return current ? unwrapNullable(current).property : undefined;
|
|
71
|
+
}
|
|
72
|
+
/** Registers the contract's flags on `command`, in contract order. */
|
|
73
|
+
export function addContractFlags(command, contract, overlay = {}) {
|
|
74
|
+
for (const spec of contractFlagSpecs(contract.properties, overlay)) {
|
|
75
|
+
const property = "path" in spec ? propertyAt(contract, spec.path) : undefined;
|
|
76
|
+
if (spec.kind === "pair") {
|
|
77
|
+
command.addOption(new Option(`--${spec.on}`, spec.entry.pair?.onHelp));
|
|
78
|
+
command.addOption(new Option(`--${spec.off}`, spec.entry.pair?.offHelp));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (spec.kind === "clear") {
|
|
82
|
+
command.addOption(new Option(`--${spec.flag}`, `Clear it (sends null). Not with --${spec.target}.`));
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (spec.kind === "boolean") {
|
|
86
|
+
command.addOption(new Option(`--${spec.flag}`, helpFor(spec, property)));
|
|
87
|
+
command.addOption(new Option(`--no-${spec.flag}`, spec.entry.negateHelp ?? `Sends ${spec.path.join(".")}: false.`));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const valueName = spec.entry.valueName ?? (spec.kind === "json" ? "<json>" : "<value>");
|
|
91
|
+
command.addOption(new Option(`--${spec.flag} ${valueName}`, helpFor(spec, property)));
|
|
92
|
+
for (const alias of spec.entry.aliases ?? [])
|
|
93
|
+
command.addOption(new Option(`--${alias} ${valueName}`).hideHelp());
|
|
94
|
+
}
|
|
95
|
+
return command;
|
|
96
|
+
}
|
|
97
|
+
function invalid(flag, value, message, code = "invalid_number") {
|
|
98
|
+
return new OxygenError(code, message, { details: { flag: `--${flag}`, value }, exitCode: 1 });
|
|
99
|
+
}
|
|
100
|
+
function parseScalar(property, raw, flag) {
|
|
101
|
+
if (!("type" in property))
|
|
102
|
+
return raw;
|
|
103
|
+
if (property.type === "integer" || property.type === "number") {
|
|
104
|
+
const parsed = Number(raw);
|
|
105
|
+
const whole = property.type === "integer";
|
|
106
|
+
const minimum = property.minimum;
|
|
107
|
+
const ok = whole ? Number.isSafeInteger(parsed) : Number.isFinite(parsed);
|
|
108
|
+
if (!ok || (minimum !== undefined && parsed < minimum)) {
|
|
109
|
+
throw invalid(flag, raw, minimum === 0
|
|
110
|
+
? `Expected a non-negative ${whole ? "integer" : "number"}.`
|
|
111
|
+
: `Expected ${whole ? "an integer" : "a number"}${minimum !== undefined ? ` of at least ${minimum}` : ""}.`);
|
|
112
|
+
}
|
|
113
|
+
// A clamped field is lowered (and reported) by the server; anything else over the maximum is refused here.
|
|
114
|
+
if (property.maximum !== undefined && parsed > property.maximum && !property["x-clamp"]) {
|
|
115
|
+
throw invalid(flag, raw, `Expected at most ${property.maximum}.`);
|
|
116
|
+
}
|
|
117
|
+
return parsed;
|
|
118
|
+
}
|
|
119
|
+
if (property.type === "string" && property.enum && !property.enum.includes(raw)) {
|
|
120
|
+
throw invalid(flag, raw, `--${flag} must be one of: ${property.enum.join(", ")}.`, "invalid_option");
|
|
121
|
+
}
|
|
122
|
+
return raw;
|
|
123
|
+
}
|
|
124
|
+
function readValue(spec, raw) {
|
|
125
|
+
if (spec.entry.parse)
|
|
126
|
+
return spec.entry.parse(raw);
|
|
127
|
+
const property = spec.property;
|
|
128
|
+
if ("type" in property && property.type === "array") {
|
|
129
|
+
return raw.split(",").map((part) => part.trim()).filter(Boolean)
|
|
130
|
+
.map((part) => parseScalar(unwrapNullable(property.items).property, part, spec.flag));
|
|
131
|
+
}
|
|
132
|
+
return parseScalar(property, raw, spec.flag);
|
|
133
|
+
}
|
|
134
|
+
function setPath(body, path, value) {
|
|
135
|
+
let cursor = body;
|
|
136
|
+
for (const key of path.slice(0, -1)) {
|
|
137
|
+
const next = cursor[key];
|
|
138
|
+
if (typeof next === "object" && next !== null && !Array.isArray(next))
|
|
139
|
+
cursor = next;
|
|
140
|
+
else
|
|
141
|
+
cursor = (cursor[key] = {});
|
|
142
|
+
}
|
|
143
|
+
cursor[path[path.length - 1]] = value;
|
|
144
|
+
}
|
|
145
|
+
function stringOption(options, flag, aliases = []) {
|
|
146
|
+
for (const name of [flag, ...aliases]) {
|
|
147
|
+
const value = options[attribute(name)];
|
|
148
|
+
if (typeof value === "string" && value.trim())
|
|
149
|
+
return value.trim();
|
|
150
|
+
}
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The request body the parsed flags describe: only the fields given, nested as
|
|
155
|
+
* the contract nests them. An object none of whose flags was given is absent.
|
|
156
|
+
*/
|
|
157
|
+
export function readContractFlags(options, contract, overlay = {}) {
|
|
158
|
+
const body = {};
|
|
159
|
+
const specs = contractFlagSpecs(contract.properties, overlay);
|
|
160
|
+
const given = new Set();
|
|
161
|
+
for (const spec of specs) {
|
|
162
|
+
if (spec.kind === "value") {
|
|
163
|
+
const raw = stringOption(options, spec.flag, spec.entry.aliases);
|
|
164
|
+
if (raw === undefined)
|
|
165
|
+
continue;
|
|
166
|
+
setPath(body, spec.path, readValue(spec, raw));
|
|
167
|
+
given.add(spec.flag);
|
|
168
|
+
}
|
|
169
|
+
else if (spec.kind === "json") {
|
|
170
|
+
const raw = stringOption(options, spec.flag, spec.entry.aliases);
|
|
171
|
+
if (raw === undefined)
|
|
172
|
+
continue;
|
|
173
|
+
let parsed;
|
|
174
|
+
try {
|
|
175
|
+
parsed = JSON.parse(raw);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
throw invalid(spec.flag, raw, `--${spec.flag} must be a JSON object.`, "invalid_json");
|
|
179
|
+
}
|
|
180
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
181
|
+
throw invalid(spec.flag, raw, `--${spec.flag} must be a JSON object.`, "invalid_json");
|
|
182
|
+
}
|
|
183
|
+
setPath(body, spec.path, parsed);
|
|
184
|
+
given.add(spec.flag);
|
|
185
|
+
}
|
|
186
|
+
else if (spec.kind === "boolean") {
|
|
187
|
+
const value = options[attribute(spec.flag)];
|
|
188
|
+
if (typeof value === "boolean")
|
|
189
|
+
setPath(body, spec.path, value);
|
|
190
|
+
}
|
|
191
|
+
else if (spec.kind === "pair") {
|
|
192
|
+
if (options[attribute(spec.off)] === true)
|
|
193
|
+
setPath(body, spec.path, false);
|
|
194
|
+
else if (options[attribute(spec.on)] === true)
|
|
195
|
+
setPath(body, spec.path, true);
|
|
196
|
+
}
|
|
197
|
+
else if (options[attribute(spec.flag)] === true) {
|
|
198
|
+
if (given.has(spec.target)) {
|
|
199
|
+
throw new OxygenError("invalid_request", `Pass either --${spec.target} or --${spec.flag}, not both.`, { exitCode: 1 });
|
|
200
|
+
}
|
|
201
|
+
setPath(body, spec.path, null);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return body;
|
|
205
|
+
}
|
|
206
|
+
/** Every long flag `addContractFlags` registers, in order (for tests and docs). */
|
|
207
|
+
export function contractFlagNames(contract, overlay = {}) {
|
|
208
|
+
return contractFlagSpecs(contract.properties, overlay).flatMap((spec) => {
|
|
209
|
+
switch (spec.kind) {
|
|
210
|
+
case "pair":
|
|
211
|
+
return [`--${spec.on}`, `--${spec.off}`];
|
|
212
|
+
case "boolean":
|
|
213
|
+
return [`--${spec.flag}`, `--no-${spec.flag}`];
|
|
214
|
+
case "clear":
|
|
215
|
+
return [`--${spec.flag}`];
|
|
216
|
+
default:
|
|
217
|
+
return [`--${spec.flag}`, ...(spec.entry.aliases ?? []).map((alias) => `--${alias}`)];
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -77,4 +77,6 @@ export declare function pickProfileNameForUserSession(userId: string, candidateS
|
|
|
77
77
|
export declare function switchCredentialProfile(profile: string, env?: NodeJS.ProcessEnv): Promise<CredentialProfile>;
|
|
78
78
|
export declare function updateActiveOrganizationForProfile(profile: string, organization: StoredOrganization, env?: NodeJS.ProcessEnv): Promise<CredentialProfile>;
|
|
79
79
|
export declare function normalizeApiUrl(value: string): string;
|
|
80
|
+
/** The CLI's per-user config directory; `OXYGEN_CONFIG_DIR` overrides it. */
|
|
81
|
+
export declare function resolveConfigDir(env?: NodeJS.ProcessEnv): string;
|
|
80
82
|
export {};
|
package/dist/credentials.js
CHANGED
|
@@ -411,12 +411,15 @@ function credentialStore(env) {
|
|
|
411
411
|
return "credential-manager";
|
|
412
412
|
return "file";
|
|
413
413
|
}
|
|
414
|
-
|
|
415
|
-
|
|
414
|
+
/** The CLI's per-user config directory; `OXYGEN_CONFIG_DIR` overrides it. */
|
|
415
|
+
export function resolveConfigDir(env = process.env) {
|
|
416
|
+
return env.OXYGEN_CONFIG_DIR?.trim()
|
|
416
417
|
|| (process.platform === "win32"
|
|
417
418
|
? join(env.APPDATA || join(homedir(), "AppData", "Roaming"), "oxygen")
|
|
418
419
|
: join(homedir(), ".config", "oxygen"));
|
|
419
|
-
|
|
420
|
+
}
|
|
421
|
+
function credentialsPath(env) {
|
|
422
|
+
return join(resolveConfigDir(env), "credentials.json");
|
|
420
423
|
}
|
|
421
424
|
function readFileCredentialPayload(env) {
|
|
422
425
|
const path = credentialsPath(env);
|
|
@@ -47,7 +47,7 @@ export function registerFunctionsCommands(program, handle) {
|
|
|
47
47
|
functions.command("rename <function> <name>").description("Rename a Function while preserving its identity, callers and published execution.")
|
|
48
48
|
.option("--json", "Print a JSON envelope.")
|
|
49
49
|
.action((ref, name, options) => handle("functions rename", options, () => requestOxygen("/api/cli/functions", { method: "POST", body: { operation: "rename", function: ref, display_name: name } })));
|
|
50
|
-
functions.command("delete <function>").description("Preview removal from the Function library, then remove it with --yes; preserves backing tables, rows and run history.
|
|
50
|
+
functions.command("delete <function>").description("Preview removal from the Function library, then remove it with --yes; preserves backing tables, rows and run history. The preview names every column that calls it: those stop running, keeping the values they already wrote. Only the Function's own active runs block deletion.")
|
|
51
51
|
.addHelpText("after", "\nDeleting your own Function is final: there is no restore, unlike `tables delete`. To reuse the logic, `tables duplicate` the backing Table and draft a new Function from the copy.\n")
|
|
52
52
|
.option("--yes", "Confirm removal of this Function after reviewing its callers.")
|
|
53
53
|
.option("--json", "Print a JSON envelope.")
|
package/dist/http-client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OXYGEN_VERSION, OxygenError, isCliResult, isVersionGreater, parseSemver, readEnvelopeCompatibility, vercelProtectionBypassHeaders, withRetryAfterDetails, } from "@oxygen/shared";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { recordMutatingRequestAnswered, recoverFromCliFloorRejection } from "./auto-update.js";
|
|
3
4
|
import { defaultApiUrl, loadCredentialsWithEndpoint } from "./credentials.js";
|
|
4
5
|
import { assertResolvedApiUrl, cliEndpointDetails, describeCliEndpoint, isProdApiUrl, resolveCliUpdateGuidance, } from "./runtime.js";
|
|
5
6
|
const DEFAULT_REQUEST_TIMEOUT_MS = 120_000;
|
|
@@ -31,7 +32,14 @@ path, options = {}) {
|
|
|
31
32
|
...init, signal: init?.signal ? AbortSignal.any([options.signal, init.signal]) : options.signal,
|
|
32
33
|
}) : baseFetch;
|
|
33
34
|
if (path !== "/api/health" && shouldCheckCliCompatibility(options)) {
|
|
34
|
-
|
|
35
|
+
try {
|
|
36
|
+
await ensureFreshCliForApiUrl(apiUrl, { fetch: fetchImpl, endpoint });
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// The preflight dialed only /api/health: nothing has run yet, so an
|
|
40
|
+
// automatic update may re-run this command in the new binary.
|
|
41
|
+
throw error instanceof OxygenError ? await recoverFromCliFloorRejection(error, endpoint) : error;
|
|
42
|
+
}
|
|
35
43
|
}
|
|
36
44
|
options.signal?.throwIfAborted();
|
|
37
45
|
const traceId = options.traceId ?? randomUUID();
|
|
@@ -117,17 +125,33 @@ path, options = {}) {
|
|
|
117
125
|
}
|
|
118
126
|
const envelope = await readEnvelope(response);
|
|
119
127
|
const compatibility = readEnvelopeCompatibility(envelope);
|
|
120
|
-
|
|
128
|
+
const failed = !response.ok || !envelope.ok;
|
|
129
|
+
try {
|
|
130
|
+
assertCliMeetsMinimumApiVersion(compatibility, options, endpoint);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// Only a refused request is safe to re-run: a successful one already did its work.
|
|
134
|
+
throw failed && error instanceof OxygenError ? await recoverFromCliFloorRejection(error, endpoint) : error;
|
|
135
|
+
}
|
|
121
136
|
warnIfCliIsOlderThanApi(compatibility.version, endpoint);
|
|
122
|
-
if (
|
|
137
|
+
if (failed) {
|
|
123
138
|
const failure = envelope;
|
|
124
139
|
const responseTraceId = response.headers.get("x-oxygen-trace-id") ?? traceId;
|
|
125
140
|
const details = withRetryAfterDetails(failure.error.details, response);
|
|
126
|
-
|
|
141
|
+
const error = new OxygenError(failure.error.code, describeFailureAgainstEndpoint(failure.error.code, failure.error.message, endpoint), {
|
|
127
142
|
details: withTraceDetails(details, responseTraceId, compatibility, endpoint),
|
|
128
143
|
exitCode: 1,
|
|
129
144
|
});
|
|
145
|
+
// The floor gate refuses before the route does any work (assertCliClientVersionFloor).
|
|
146
|
+
if (error.code === "cli_update_required")
|
|
147
|
+
throw await recoverFromCliFloorRejection(error, endpoint);
|
|
148
|
+
// Any other answer to a write may have written something; never re-run the command after it.
|
|
149
|
+
if (method !== "GET")
|
|
150
|
+
recordMutatingRequestAnswered();
|
|
151
|
+
throw error;
|
|
130
152
|
}
|
|
153
|
+
if (method !== "GET")
|
|
154
|
+
recordMutatingRequestAnswered();
|
|
131
155
|
return envelope.data;
|
|
132
156
|
}
|
|
133
157
|
async function readBoundedBinaryResponse(response, maxBytes) {
|