@oxygen-agent/cli 1.275.1 → 1.284.3
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/command-manifest.d.ts +30 -0
- package/dist/command-manifest.js +70 -0
- package/dist/help.d.ts +2 -0
- package/dist/help.js +79 -0
- package/dist/http-client.d.ts +1 -0
- package/dist/http-client.js +13 -0
- package/dist/index.js +388 -24
- package/dist/knowledge-mirror.d.ts +22 -0
- package/dist/knowledge-mirror.js +56 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/billing.js +6 -0
- package/node_modules/@oxygen/shared/dist/cli-result.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +65 -0
- package/node_modules/@oxygen/shared/dist/deprecation-registry.d.ts +12 -0
- package/node_modules/@oxygen/shared/dist/deprecation-registry.js +58 -0
- package/node_modules/@oxygen/shared/dist/knowledge-markdown.d.ts +25 -0
- package/node_modules/@oxygen/shared/dist/knowledge-markdown.js +74 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -92,6 +92,28 @@ export declare function acquireMirrorLock(dir: string, options?: {
|
|
|
92
92
|
export declare function releaseMirrorLock(dir: string): void;
|
|
93
93
|
export declare function writeGeneratedIndexFile(dir: string, index: MirrorIndexSnapshot): void;
|
|
94
94
|
export declare function writeGeneratedLogFile(dir: string, entries: MirrorLogEntry[]): void;
|
|
95
|
+
export type MirrorPushCandidate = {
|
|
96
|
+
slug: string;
|
|
97
|
+
/** dirty = tracked page whose local bytes drifted; new = untracked local file. */
|
|
98
|
+
kind: "dirty" | "new";
|
|
99
|
+
path: string;
|
|
100
|
+
/** Manifest record for dirty pages — its revision is the optimistic-concurrency base. */
|
|
101
|
+
record?: MirrorPageRecord;
|
|
102
|
+
};
|
|
103
|
+
export type MirrorPushPlan = {
|
|
104
|
+
candidates: MirrorPushCandidate[];
|
|
105
|
+
skipped: Array<{
|
|
106
|
+
file: string;
|
|
107
|
+
reason: "invalid_slug" | "reserved_slug" | "generated_file";
|
|
108
|
+
}>;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Scan the mirror for pushable local state: tracked pages whose bytes drifted
|
|
112
|
+
* from the manifest sha (edited locally) and untracked `.md` files (created
|
|
113
|
+
* locally). Generated index/log summaries and reserved slugs are skipped.
|
|
114
|
+
* Read-only — the push command decides what to do with the plan.
|
|
115
|
+
*/
|
|
116
|
+
export declare function planMirrorPush(dir: string, state: MirrorState | null): MirrorPushPlan;
|
|
95
117
|
/** Delete the whole mirror (pages, generated files, sidecar). Safe on a missing dir. */
|
|
96
118
|
export declare function purgeMirror(dir: string): void;
|
|
97
119
|
/**
|
package/dist/knowledge-mirror.js
CHANGED
|
@@ -327,6 +327,62 @@ function writeGeneratedFile(dir, name, lines) {
|
|
|
327
327
|
function oneLine(value) {
|
|
328
328
|
return value.replace(/\s+/g, " ").trim();
|
|
329
329
|
}
|
|
330
|
+
// Slugs the server write path refuses (RESERVED_KNOWLEDGE_SLUGS) — an untracked
|
|
331
|
+
// local file under one of these names can never push. A TRACKED page under one
|
|
332
|
+
// (legacy rows may predate the reservation) still pushes by id.
|
|
333
|
+
const PUSH_RESERVED_SLUGS = new Set(["index", "log", "schema", "company-profile", "readme"]);
|
|
334
|
+
/**
|
|
335
|
+
* Scan the mirror for pushable local state: tracked pages whose bytes drifted
|
|
336
|
+
* from the manifest sha (edited locally) and untracked `.md` files (created
|
|
337
|
+
* locally). Generated index/log summaries and reserved slugs are skipped.
|
|
338
|
+
* Read-only — the push command decides what to do with the plan.
|
|
339
|
+
*/
|
|
340
|
+
export function planMirrorPush(dir, state) {
|
|
341
|
+
const plan = { candidates: [], skipped: [] };
|
|
342
|
+
let entries = [];
|
|
343
|
+
try {
|
|
344
|
+
entries = readdirSync(dir);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return plan; // no mirror directory — nothing to push
|
|
348
|
+
}
|
|
349
|
+
for (const entry of entries.sort((a, b) => a.localeCompare(b))) {
|
|
350
|
+
if (!entry.endsWith(".md"))
|
|
351
|
+
continue;
|
|
352
|
+
const slug = entry.slice(0, -3);
|
|
353
|
+
const path = join(dir, entry);
|
|
354
|
+
const record = state?.pages[slug];
|
|
355
|
+
if (record) {
|
|
356
|
+
if (isFileDirty(dir, slug, record.content_sha256)) {
|
|
357
|
+
plan.candidates.push({ slug, kind: "dirty", path, record });
|
|
358
|
+
}
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (slug.length > MIRROR_SLUG_MAX_LENGTH || !MIRROR_SLUG_PATTERN.test(slug)) {
|
|
362
|
+
plan.skipped.push({ file: entry, reason: "invalid_slug" });
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
// Untracked generated summaries (index.md / log.md before their slugs are
|
|
366
|
+
// ever page-backed) carry the marker header — never push those.
|
|
367
|
+
let head = "";
|
|
368
|
+
try {
|
|
369
|
+
head = readFileSync(path, "utf8").slice(0, GENERATED_MIRROR_FILE_HEADER.length);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (head === GENERATED_MIRROR_FILE_HEADER) {
|
|
375
|
+
plan.skipped.push({ file: entry, reason: "generated_file" });
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (PUSH_RESERVED_SLUGS.has(slug)) {
|
|
379
|
+
plan.skipped.push({ file: entry, reason: "reserved_slug" });
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
plan.candidates.push({ slug, kind: "new", path });
|
|
383
|
+
}
|
|
384
|
+
return plan;
|
|
385
|
+
}
|
|
330
386
|
// ── Purge ───────────────────────────────────────────────────────────────────────
|
|
331
387
|
/** Delete the whole mirror (pages, generated files, sidecar). Safe on a missing dir. */
|
|
332
388
|
export function purgeMirror(dir) {
|
|
@@ -26,6 +26,8 @@ export type PricingPlanDefinition = {
|
|
|
26
26
|
};
|
|
27
27
|
export type PricingPlanMetadata = Record<string, unknown> | null | undefined;
|
|
28
28
|
export declare const CONTACT_SALES_URL = "https://cal.com/tim-scheuer-mxbib9/45";
|
|
29
|
+
export declare const TRIAL_PERIOD_DAYS = 7;
|
|
30
|
+
export declare const TRIAL_CREDIT_GRANT = 1000;
|
|
29
31
|
export declare const BASE_PRICING_PLANS: {
|
|
30
32
|
readonly free: {
|
|
31
33
|
readonly tier: "free";
|
|
@@ -2,6 +2,12 @@ export const WEEKLY_USAGE_WINDOW_DAYS = 7;
|
|
|
2
2
|
export const BILLING_CURRENCIES = ["usd"];
|
|
3
3
|
export const DEFAULT_BILLING_CURRENCY = "usd";
|
|
4
4
|
export const CONTACT_SALES_URL = "https://cal.com/tim-scheuer-mxbib9/45";
|
|
5
|
+
// Card-required free trial: every new signup starts a Stripe trial on the Starter
|
|
6
|
+
// plan, gets TRIAL_CREDIT_GRANT credits (a value-demonstration budget sized to the
|
|
7
|
+
// guided first outcome — NOT the full plan allowance), and converts to paid on
|
|
8
|
+
// credit exhaustion or when the trial clock elapses, whichever comes first.
|
|
9
|
+
export const TRIAL_PERIOD_DAYS = 7;
|
|
10
|
+
export const TRIAL_CREDIT_GRANT = 1_000;
|
|
5
11
|
export const BASE_PRICING_PLANS = {
|
|
6
12
|
free: {
|
|
7
13
|
tier: "free",
|
|
@@ -30,6 +30,9 @@ export declare class OxygenError extends Error {
|
|
|
30
30
|
exitCode?: number;
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
export declare const CLI_EXIT_CODE_TABLE: Readonly<Record<number, string>>;
|
|
34
|
+
export declare function exitCodeForErrorCode(code: string): number;
|
|
35
|
+
export declare function exitCodeForOxygenError(error: OxygenError): number;
|
|
33
36
|
export declare function success<T>(command: string, data: T, version?: string, minimumCliVersion?: string): CliSuccess<T>;
|
|
34
37
|
export declare function failure(command: string, error: {
|
|
35
38
|
code: string;
|
|
@@ -19,6 +19,71 @@ export class OxygenError extends Error {
|
|
|
19
19
|
this.exitCode = options.exitCode ?? 1;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
// Reserved CLI exit codes, so shell agents can branch on failure class without
|
|
23
|
+
// parsing the JSON envelope. Documented in `oxygen --help` and in the
|
|
24
|
+
// `oxygen commands --json` manifest; treat as a stable contract.
|
|
25
|
+
//
|
|
26
|
+
// 0 ok · 1 unclassified error · 2 usage / input validation · 3 auth or CLI
|
|
27
|
+
// compatibility (not_authenticated, cli_update_required) · 4 not found ·
|
|
28
|
+
// 5 provider / server failure · 6 rate limit (details.retry_after_seconds
|
|
29
|
+
// says how long to wait) · 7 approval / spend gate (re-run with --approved
|
|
30
|
+
// --max-credits <n>) · 8 timeout.
|
|
31
|
+
export const CLI_EXIT_CODE_TABLE = {
|
|
32
|
+
0: "ok",
|
|
33
|
+
1: "unclassified error",
|
|
34
|
+
2: "usage / input validation",
|
|
35
|
+
3: "auth or CLI compatibility",
|
|
36
|
+
4: "not found",
|
|
37
|
+
5: "provider or server failure",
|
|
38
|
+
6: "rate limited (wait details.retry_after_seconds, then retry)",
|
|
39
|
+
7: "approval / spend gate (re-run with --approved --max-credits <n>)",
|
|
40
|
+
8: "timeout",
|
|
41
|
+
};
|
|
42
|
+
export function exitCodeForErrorCode(code) {
|
|
43
|
+
switch (code) {
|
|
44
|
+
case "not_authenticated":
|
|
45
|
+
case "unauthorized":
|
|
46
|
+
case "invalid_token":
|
|
47
|
+
case "cli_update_required":
|
|
48
|
+
return 3;
|
|
49
|
+
case "rate_limit_exceeded":
|
|
50
|
+
return 6;
|
|
51
|
+
case "max_credits_required":
|
|
52
|
+
case "approval_required":
|
|
53
|
+
case "spend_cap_required":
|
|
54
|
+
case "spend_cap_too_low":
|
|
55
|
+
case "insufficient_credits":
|
|
56
|
+
case "workspace_credit_cap_exceeded":
|
|
57
|
+
case "byok_requires_paid_plan":
|
|
58
|
+
return 7;
|
|
59
|
+
case "network_timeout":
|
|
60
|
+
return 8;
|
|
61
|
+
case "provider_error":
|
|
62
|
+
case "upstream_error":
|
|
63
|
+
case "internal_error":
|
|
64
|
+
case "server_error":
|
|
65
|
+
return 5;
|
|
66
|
+
default:
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
if (code === "not_found" || code.endsWith("_not_found"))
|
|
70
|
+
return 4;
|
|
71
|
+
if (code === "timeout" || code.endsWith("_timeout"))
|
|
72
|
+
return 8;
|
|
73
|
+
if (code.startsWith("invalid_")
|
|
74
|
+
|| code.startsWith("missing_")
|
|
75
|
+
|| code === "unknown_fields"
|
|
76
|
+
|| code === "validation_failed") {
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
// exitCode 1 is the legacy constructor default at ~170 OxygenError call sites;
|
|
82
|
+
// treat a stored 1 as "unset" and derive the reserved code from the typed
|
|
83
|
+
// error code instead. An explicit non-1 exitCode option always wins.
|
|
84
|
+
export function exitCodeForOxygenError(error) {
|
|
85
|
+
return error.exitCode !== 1 ? error.exitCode : exitCodeForErrorCode(error.code);
|
|
86
|
+
}
|
|
22
87
|
export function success(command, data, version = OXYGEN_VERSION, minimumCliVersion = OXYGEN_MINIMUM_CLI_VERSION) {
|
|
23
88
|
return {
|
|
24
89
|
ok: true,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DeprecationRegistryEntry = {
|
|
2
|
+
/** What is deprecated, precisely enough to grep for. */
|
|
3
|
+
surface: string;
|
|
4
|
+
/** OXYGEN_VERSION at which the surface became deprecated. */
|
|
5
|
+
deprecated_in: string;
|
|
6
|
+
/** Semver deadline, or "floor-bump" (see header). */
|
|
7
|
+
sunset_version: string;
|
|
8
|
+
/** Where the surface lives and what removal entails. */
|
|
9
|
+
note: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const FLOOR_BUMP_SENTINEL = "floor-bump";
|
|
12
|
+
export declare const DEPRECATION_REGISTRY: DeprecationRegistryEntry[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Deprecation sunset registry — the build-enforced list of deprecated agent
|
|
2
|
+
// surfaces and when each one must actually be removed.
|
|
3
|
+
//
|
|
4
|
+
// Why this exists: deprecated aliases ("kept for old clients") historically
|
|
5
|
+
// outlive every intention to remove them because nothing ever fails. The
|
|
6
|
+
// registry inverts that: packages/shared/src/index.test.ts asserts that
|
|
7
|
+
// OXYGEN_VERSION has NOT passed any entry's semver `sunset_version`. Once the
|
|
8
|
+
// version crosses a sunset, the build goes red until the surface is deleted —
|
|
9
|
+
// and removal means deleting the surface AND its registry entry in the same
|
|
10
|
+
// PR.
|
|
11
|
+
//
|
|
12
|
+
// sunset_version values:
|
|
13
|
+
// - "1.x.y" semver — hard deadline, gated by the test.
|
|
14
|
+
// - "floor-bump" — sentinel for surfaces that can only be removed when
|
|
15
|
+
// OXYGEN_MINIMUM_CLI_VERSION next rises past the clients that still use
|
|
16
|
+
// them (old CLIs on users' machines send/read these). The test validates
|
|
17
|
+
// the entry's shape but skips the version gate; whoever bumps the CLI
|
|
18
|
+
// floor must sweep the "floor-bump" entries in the same change.
|
|
19
|
+
export const FLOOR_BUMP_SENTINEL = "floor-bump";
|
|
20
|
+
export const DEPRECATION_REGISTRY = [
|
|
21
|
+
{
|
|
22
|
+
surface: "oxygen_templates_* MCP alias tools",
|
|
23
|
+
deprecated_in: "1.80.0",
|
|
24
|
+
sunset_version: "1.290.0",
|
|
25
|
+
note: "Deprecated alias tree of oxygen_prompts_* (packages/mcp-server/src/tools/" +
|
|
26
|
+
"prompt-template-tools.ts, prefix \"oxygen_templates\"). Removal: drop the " +
|
|
27
|
+
"alias prefix from the generated tools and the CLI `templates` alias " +
|
|
28
|
+
"command tree, then update the pinned tool counts.",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
surface: "oxygen.linkedin-inbox widget alias",
|
|
32
|
+
deprecated_in: "1.226.0",
|
|
33
|
+
sunset_version: "1.290.0",
|
|
34
|
+
note: "Legacy alias of oxygen.unibox kept so MCP clients that cached " +
|
|
35
|
+
"ui://oxygen/linkedin-inbox keep resolving (packages/mcp-server/src/widgets/" +
|
|
36
|
+
"definitions.ts). Removal: delete the alias widget definition and any " +
|
|
37
|
+
"bindings that still point at it.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
surface: "CLI deepLink/deep_link response keys",
|
|
41
|
+
deprecated_in: "1.8.2",
|
|
42
|
+
sunset_version: FLOOR_BUMP_SENTINEL,
|
|
43
|
+
note: "web_url has been the canonical deep-link key since at least v1.8.2; " +
|
|
44
|
+
"/api/cli responses still duplicate it as deepLink/deep_link because " +
|
|
45
|
+
"CLIs at or below the current minimum version read the legacy keys. " +
|
|
46
|
+
"Remove the duplicated keys (and the CLI readers) when " +
|
|
47
|
+
"OXYGEN_MINIMUM_CLI_VERSION next rises past those clients.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
surface: "CLI --return/--org legacy flags",
|
|
51
|
+
deprecated_in: "1.0.0",
|
|
52
|
+
sunset_version: FLOOR_BUMP_SENTINEL,
|
|
53
|
+
note: "Legacy aliases of --return-mode/--org-id on `oxygen tools run` " +
|
|
54
|
+
"(packages/cli/src/index.ts); aliased since before versioned commits. " +
|
|
55
|
+
"Remove the flags and their request-body mappings when " +
|
|
56
|
+
"OXYGEN_MINIMUM_CLI_VERSION next rises past CLIs that send them.",
|
|
57
|
+
},
|
|
58
|
+
];
|
|
@@ -21,3 +21,28 @@ export type KnowledgePageRenderInput = {
|
|
|
21
21
|
* verbatim with exactly one trailing newline.
|
|
22
22
|
*/
|
|
23
23
|
export declare function renderKnowledgePageMarkdown(page: KnowledgePageRenderInput): string;
|
|
24
|
+
export type ParsedKnowledgePageMarkdown = {
|
|
25
|
+
/** True when the file opened with a `---` frontmatter block (any keys). */
|
|
26
|
+
hasFrontmatter: boolean;
|
|
27
|
+
/** Raw scalar frontmatter values by key (tags stay in their `[a, b]` form here). */
|
|
28
|
+
frontmatter: Record<string, string>;
|
|
29
|
+
id: string | null;
|
|
30
|
+
slug: string | null;
|
|
31
|
+
type: string | null;
|
|
32
|
+
title: string | null;
|
|
33
|
+
status: string | null;
|
|
34
|
+
tags: string[] | null;
|
|
35
|
+
summary: string | null;
|
|
36
|
+
revision: number | null;
|
|
37
|
+
/** Body text below the frontmatter (whole file when none), no trailing newline. */
|
|
38
|
+
body: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Parse a mirror page file back into its fields — the inverse of
|
|
42
|
+
* renderKnowledgePageMarkdown, tolerant of human/agent edits (`oxygen knowledge
|
|
43
|
+
* push` runs this on locally edited files). Line-based like the renderer's
|
|
44
|
+
* contract: `key: value` pairs between `---` fences, tags as `[a, b]`. A file
|
|
45
|
+
* without a frontmatter block parses as pure body; a plain-markdown title is
|
|
46
|
+
* derived from the first `# heading` so brand-new local files can push.
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseKnowledgePageMarkdown(contents: string): ParsedKnowledgePageMarkdown;
|
|
@@ -55,3 +55,77 @@ export function renderKnowledgePageMarkdown(page) {
|
|
|
55
55
|
const body = (page.body ?? "").replace(/\r\n/g, "\n").replace(/\n+$/, "");
|
|
56
56
|
return `${lines.join("\n")}\n${body}\n`;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse a mirror page file back into its fields — the inverse of
|
|
60
|
+
* renderKnowledgePageMarkdown, tolerant of human/agent edits (`oxygen knowledge
|
|
61
|
+
* push` runs this on locally edited files). Line-based like the renderer's
|
|
62
|
+
* contract: `key: value` pairs between `---` fences, tags as `[a, b]`. A file
|
|
63
|
+
* without a frontmatter block parses as pure body; a plain-markdown title is
|
|
64
|
+
* derived from the first `# heading` so brand-new local files can push.
|
|
65
|
+
*/
|
|
66
|
+
export function parseKnowledgePageMarkdown(contents) {
|
|
67
|
+
const text = contents.replace(/\r\n/g, "\n");
|
|
68
|
+
const lines = text.split("\n");
|
|
69
|
+
const parsed = {
|
|
70
|
+
hasFrontmatter: false,
|
|
71
|
+
frontmatter: {},
|
|
72
|
+
id: null,
|
|
73
|
+
slug: null,
|
|
74
|
+
type: null,
|
|
75
|
+
title: null,
|
|
76
|
+
status: null,
|
|
77
|
+
tags: null,
|
|
78
|
+
summary: null,
|
|
79
|
+
revision: null,
|
|
80
|
+
body: "",
|
|
81
|
+
};
|
|
82
|
+
let bodyStart = 0;
|
|
83
|
+
if (lines[0]?.trim() === "---") {
|
|
84
|
+
const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
85
|
+
if (closing > 0) {
|
|
86
|
+
parsed.hasFrontmatter = true;
|
|
87
|
+
for (const line of lines.slice(1, closing)) {
|
|
88
|
+
const separator = line.indexOf(":");
|
|
89
|
+
if (separator <= 0)
|
|
90
|
+
continue;
|
|
91
|
+
const key = line.slice(0, separator).trim();
|
|
92
|
+
if (!/^[A-Za-z0-9_-]+$/.test(key))
|
|
93
|
+
continue;
|
|
94
|
+
parsed.frontmatter[key] = line.slice(separator + 1).trim();
|
|
95
|
+
}
|
|
96
|
+
bodyStart = closing + 1;
|
|
97
|
+
// The renderer emits exactly one blank line after the closing fence.
|
|
98
|
+
if (lines[bodyStart] === "")
|
|
99
|
+
bodyStart += 1;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
parsed.body = lines.slice(bodyStart).join("\n").replace(/\n+$/, "");
|
|
103
|
+
const fm = parsed.frontmatter;
|
|
104
|
+
const str = (key) => {
|
|
105
|
+
const value = fm[key];
|
|
106
|
+
return value !== undefined && value !== "" ? value : null;
|
|
107
|
+
};
|
|
108
|
+
parsed.id = str("id");
|
|
109
|
+
parsed.slug = str("slug");
|
|
110
|
+
parsed.type = str("type");
|
|
111
|
+
parsed.title = str("title");
|
|
112
|
+
parsed.status = str("status");
|
|
113
|
+
parsed.summary = str("summary");
|
|
114
|
+
if (fm.tags !== undefined) {
|
|
115
|
+
const inner = fm.tags.replace(/^\[/, "").replace(/\]$/, "");
|
|
116
|
+
parsed.tags = inner
|
|
117
|
+
.split(",")
|
|
118
|
+
.map((tag) => tag.trim())
|
|
119
|
+
.filter((tag) => tag.length > 0);
|
|
120
|
+
}
|
|
121
|
+
if (fm.revision !== undefined) {
|
|
122
|
+
const revision = Number.parseInt(fm.revision, 10);
|
|
123
|
+
parsed.revision = Number.isFinite(revision) ? revision : null;
|
|
124
|
+
}
|
|
125
|
+
if (!parsed.title) {
|
|
126
|
+
const heading = lines.slice(bodyStart).find((line) => /^#\s+\S/.test(line));
|
|
127
|
+
if (heading)
|
|
128
|
+
parsed.title = heading.replace(/^#\s+/, "").trim() || null;
|
|
129
|
+
}
|
|
130
|
+
return parsed;
|
|
131
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const OXYGEN_VERSION = "1.
|
|
1
|
+
export declare const OXYGEN_VERSION = "1.284.3";
|
|
2
2
|
export declare const OXYGEN_MINIMUM_CLI_VERSION = "1.181.0";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const OXYGEN_VERSION = "1.
|
|
1
|
+
export const OXYGEN_VERSION = "1.284.3";
|
|
2
2
|
// Bump this only when deployed CLI/API contracts require a newer CLI.
|
|
3
3
|
// 1.181.0: paid table action runs and background columns run require
|
|
4
4
|
// approved=true in addition to max_credits; older CLIs cannot send the flag.
|