@oxygen-agent/cli 1.1014.19 → 1.1014.81
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.js +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.js +25 -3
- package/dist/index.js +81 -16
- package/dist/json-output.d.ts +3 -0
- package/dist/json-output.js +33 -0
- package/dist/workspace-skill-commands.d.ts +9 -0
- package/dist/workspace-skill-commands.js +135 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +9 -0
- package/node_modules/@oxygen/shared/dist/column-decision.js +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/operations.js +4 -3
- package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/contracts/table-query-grid.js +7 -4
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.d.ts +14 -0
- package/node_modules/@oxygen/shared/dist/contracts/table-query-raw.js +18 -0
- package/node_modules/@oxygen/shared/dist/contracts/workspace-skills.d.ts +6 -0
- package/node_modules/@oxygen/shared/dist/contracts/workspace-skills.js +62 -0
- package/node_modules/@oxygen/shared/dist/dev-eval-classifier.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/dev-eval-classifier.js +4 -0
- package/node_modules/@oxygen/shared/dist/dev-eval-context.d.ts +35 -0
- package/node_modules/@oxygen/shared/dist/dev-eval-context.js +108 -0
- package/node_modules/@oxygen/shared/dist/diagnostic-storage-lock.js +5 -1
- package/node_modules/@oxygen/shared/dist/diagnostic-storage.js +178 -63
- package/node_modules/@oxygen/shared/dist/email-health-status.d.ts +11 -0
- package/node_modules/@oxygen/shared/dist/email-health-status.js +16 -0
- package/node_modules/@oxygen/shared/dist/hosted-ai.d.ts +3 -2
- package/node_modules/@oxygen/shared/dist/hosted-ai.js +9 -6
- package/node_modules/@oxygen/shared/dist/langfuse.d.ts +4 -2
- package/node_modules/@oxygen/shared/dist/langfuse.js +123 -66
- package/node_modules/@oxygen/shared/dist/table-run-summary.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/table-run-summary.js +26 -0
- 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/workspace-skills.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/workspace-skills.js +102 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/command-manifest.js
CHANGED
|
@@ -306,7 +306,7 @@ export function searchCommandManifest(manifest, query, requestedLimit = DEFAULT_
|
|
|
306
306
|
commands: matches.slice(0, limit).map(({ command, score }) => ({
|
|
307
307
|
name: command.name,
|
|
308
308
|
group: command.group,
|
|
309
|
-
description: command.description,
|
|
309
|
+
description: (command.description.split(/(?<=[.!?])\s/)[0] ?? command.description).slice(0, 180),
|
|
310
310
|
spends_credits: command.spends_credits,
|
|
311
311
|
mutates: command.mutates,
|
|
312
312
|
destructive: command.destructive,
|
package/dist/help.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CommanderError, type Command } from "commander";
|
|
2
2
|
import { type CliFailure } from "@oxygen/shared/cli-result";
|
|
3
|
-
export declare function applyOxygenHelp(program: Command, binaryName: string): void;
|
|
3
|
+
export declare function applyOxygenHelp(program: Command, binaryName: string, docsUrl?: string): void;
|
|
4
4
|
/**
|
|
5
5
|
* Turn commander's own edit-distance suggester on for EVERY command.
|
|
6
6
|
*
|
package/dist/help.js
CHANGED
|
@@ -125,7 +125,16 @@ const HELP_GROUPS = [
|
|
|
125
125
|
},
|
|
126
126
|
];
|
|
127
127
|
const FALLBACK_HEADING = "Other commands:";
|
|
128
|
-
export function applyOxygenHelp(program, binaryName) {
|
|
128
|
+
export function applyOxygenHelp(program, binaryName, docsUrl = "https://oxygen-agent.com/docs") {
|
|
129
|
+
program.option("--help-full", "Show expanded command and option descriptions.");
|
|
130
|
+
const configure = (command) => {
|
|
131
|
+
command.configureHelp({
|
|
132
|
+
subcommandDescription: (cmd) => shortHelpDescription(cmd.description()),
|
|
133
|
+
});
|
|
134
|
+
for (const child of command.commands)
|
|
135
|
+
configure(child);
|
|
136
|
+
};
|
|
137
|
+
configure(program);
|
|
129
138
|
const headingByCommand = new Map();
|
|
130
139
|
for (const group of HELP_GROUPS) {
|
|
131
140
|
for (const name of group.commands)
|
|
@@ -140,6 +149,7 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
140
149
|
program.addHelpText("before", [
|
|
141
150
|
"",
|
|
142
151
|
"Agent quickstart:",
|
|
152
|
+
" Help is concise; --help-full or commands get <name> --json retains complete grammar.",
|
|
143
153
|
` 1. ${binaryName} login connect this terminal (or set OXYGEN_API_KEY)`,
|
|
144
154
|
` ${binaryName} whoami --summary-json confirm the active workspace and identity (0 credits)`,
|
|
145
155
|
// `login` auto-installs the skills, but an agent authenticating with
|
|
@@ -159,7 +169,7 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
159
169
|
` 5. ${binaryName} recipes list "<goal>" --view summary --json choose a play from the Recipe catalog for your goal`,
|
|
160
170
|
` Provider inventory: ${binaryName} tools search <brand> --json, then ${binaryName} tools search --provider <provider-id> --all --json (Blitz uses blitzapi).`,
|
|
161
171
|
` Installable systems and recurring monitors: ${binaryName} blueprints list --json.`,
|
|
162
|
-
|
|
172
|
+
` Docs: ${docsUrl}`,
|
|
163
173
|
"",
|
|
164
174
|
"Conventions:",
|
|
165
175
|
" --json prints a stable envelope {ok, data|error, meta}; errors always print it.",
|
|
@@ -171,6 +181,16 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
171
181
|
].join("\n"));
|
|
172
182
|
program.addHelpText("afterAll", `\nExit codes: ${exitCodeLine}`);
|
|
173
183
|
}
|
|
184
|
+
function shortHelpDescription(description) {
|
|
185
|
+
if (process.argv.includes("--help-full"))
|
|
186
|
+
return description;
|
|
187
|
+
// Summarize navigation cards only. Selected commands and option grammar stay
|
|
188
|
+
// complete: cutting a flag at "e.g." or dropping its mode restriction caused
|
|
189
|
+
// a dead end in the native blind acceptance. Never truncate inside examples.
|
|
190
|
+
const sentences = description.split(/(?<!\be\.g\.)(?<!\bi\.e\.)(?<=[.!?])\s+/);
|
|
191
|
+
const safety = /\b(?:credits?|read-only|free|preview\w*|approv\w*|purg\w*|permanent\w*|irreversible)\b|--(?:yes|now)\b/i;
|
|
192
|
+
return sentences.filter((sentence, index) => index === 0 || safety.test(sentence)).join(" ");
|
|
193
|
+
}
|
|
174
194
|
/**
|
|
175
195
|
* Turn commander's own edit-distance suggester on for EVERY command.
|
|
176
196
|
*
|
|
@@ -443,7 +463,9 @@ function buildUnknownCommandHint(parent, path, token) {
|
|
|
443
463
|
// command.
|
|
444
464
|
const prefixMatches = names.filter((name) => name.startsWith(lower));
|
|
445
465
|
const suggestion = synonym ?? (prefixMatches.length === 1 ? prefixMatches[0] : null);
|
|
446
|
-
const
|
|
466
|
+
const query = path.split(" ").slice(1).concat(token).join(" ");
|
|
467
|
+
const quotedQuery = `'${query.replace(/'/g, "'\\''")}'`;
|
|
468
|
+
const helpLine = `Find the command with \`${path.split(" ")[0]} commands search ${quotedQuery} --json\`; hydrate the selected name with \`${path.split(" ")[0]} commands get <name> --json\`.`;
|
|
447
469
|
return suggestion
|
|
448
470
|
? `Hint: \`${path}\` has no \`${token}\`. Did you mean \`${path} ${suggestion}\`? ${helpLine}`
|
|
449
471
|
: `Hint: \`${path}\` has no \`${token}\`. ${helpLine}`;
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { applyOxygenHelp, argvRequestsJson, commanderUsageFailure, commandNameFo
|
|
|
20
20
|
import { homeStandupMarksSeen } from "./home-standup.js";
|
|
21
21
|
import { ensureFreshCliForApiUrl, requestOxygen } from "./http-client.js";
|
|
22
22
|
import { formatInboxNeedsReplyNotice } from "./inbox-needs-reply-notice.js";
|
|
23
|
+
import { serializeCliJson, writeExclusiveJson } from "./json-output.js";
|
|
23
24
|
import { acquireMirrorLock, clearConflictFiles, deletePageFile, emptyMirrorState, findMirrorSlugByPageId, isFileDirty, listConflictFiles, listLocalMirrors, localPageSha256, markMirrorStale, mirrorExists, pageFilePath, planMirrorPush, purgeMirror, resetMirrorForFullResync, quarantineDirtyFile, readMirrorState, releaseMirrorLock, resolveDefaultConfigDir, resolveMirrorDir, writeGeneratedIndexFile, writeGeneratedLogFile, writeMirrorState, writePageFile, } from "./knowledge-mirror.js";
|
|
24
25
|
import { registerKnowledgeRepositoryCommands } from "./knowledge-repository-commands.js";
|
|
25
26
|
import { runLocalCustomHttpColumn } from "./local-custom-http-column.js";
|
|
@@ -42,6 +43,7 @@ import { registerVisualCommands } from "./visual-commands.js";
|
|
|
42
43
|
import { WHATSAPP_ACCOUNT_FLAGS, whatsappAccountQuery } from "./whatsapp-account-flags.js";
|
|
43
44
|
import { formatWorkflowPlanLimitNotices } from "./workflow-plan-limit-notices.js";
|
|
44
45
|
import { WORKFLOW_RUN_LIST_FLAG_OVERLAY, readWorkflowRunListFlags } from "./workflow-run-list-flags.js";
|
|
46
|
+
import { registerWorkspaceSkillCommands } from "./workspace-skill-commands.js";
|
|
45
47
|
import { addProviderLimitFlags, addSuppressionListFlags, providerLimitBody, providerLimitQuery, suppressionListQuery } from "./workspace-utility-flags.js";
|
|
46
48
|
import { isRecipeDefinition } from "@oxygen/recipe-sdk";
|
|
47
49
|
import { AGENCY_DIRECTORY_REGIONS, AGENCY_DIRECTORY_SERVICES, COLLAB_GATE_KINDS, COLLAB_GATE_PROSE, COLLAB_SUBJECT_KINDS, COLLAB_SUBJECT_KINDS_PROSE, COLLAB_SUBJECT_LABELS, COLLAB_SUBJECT_PROSE, GATE_KIND_SUBJECTS, describeWorkflowStatusChange, formatCellForDisplay, formatCopilotPlanDuration, formatCopilotPlanSeconds, formatPublicBudgetScopes, SUBJECT_PATH_FORMS_PROSE, formatSubjectPath, exitCodeForOxygenError, parseSubjectPath, parseSubjectRef, parseWorkflowStatusChange, chunk, isVersionGreater, isVersionLess, KNOWLEDGE_BOOTSTRAP_MAX_CREDITS, MAX_CLI_JSON_BODY_BYTES, MAX_MCP_TOOL_NAME_LENGTH, normalizeCopilotPlanStepStatus, OXYGEN_CAPABILITY_ROUTES, OXYGEN_VERSION, OxygenError, getCapabilityRouteMatch, inferUserCapabilityRoute, parseKnowledgePageMarkdown, PLAN_LIMITS, serializeCapabilityRoute, sleep, success, TAG_KINDS_PROSE, toFailure, workflowMcpToolName, } from "@oxygen/shared";
|
|
@@ -65,6 +67,7 @@ import { SEQUENCE_LIST_PROJECTION_CONTRACT } from "@oxygen/shared/contracts/sequ
|
|
|
65
67
|
import { TABLE_FEED_UPDATE_CONTRACT, tableFeedUpdateIssues } from "@oxygen/shared/contracts/table-feed-update";
|
|
66
68
|
import { TABLE_LIST_QUERY_CONTRACT, tableListQueryParams } from "@oxygen/shared/contracts/table-list";
|
|
67
69
|
import { TABLE_QUERY_GRID_CONTRACT } from "@oxygen/shared/contracts/table-query-grid";
|
|
70
|
+
import { TABLE_QUERY_CLI_DEFAULT_LIMIT, TABLE_QUERY_CLI_DEFAULT_MAX_CHARS, tableQueryRawResultFits, tableQueryRawRecovery } from "@oxygen/shared/contracts/table-query-raw";
|
|
68
71
|
import { WHATSAPP_ACCOUNTS_READ_CONTRACT } from "@oxygen/shared/contracts/whatsapp-reads";
|
|
69
72
|
import { WORKFLOW_RUN_LIST_CONTRACT, workflowRunListQueryParams } from "@oxygen/shared/contracts/workflow-run-list";
|
|
70
73
|
import { COPILOT_ASK_USER_TOOL_NAME, normalizeCopilotQuestions } from "@oxygen/shared/copilot-questions";
|
|
@@ -79,6 +82,7 @@ import { TAG_COLORS } from "@oxygen/shared/select-options";
|
|
|
79
82
|
import { SEQUENCE_LIFECYCLES, sequenceDisplayStatus } from "@oxygen/shared/sequence-lifecycle";
|
|
80
83
|
import { lookupColumnDataType } from "@oxygen/shared/table-field-types";
|
|
81
84
|
import { FILTER_TREE_OPERATORS } from "@oxygen/shared/table-filter-operators";
|
|
85
|
+
import { tableRunSummary } from "@oxygen/shared/table-run-summary";
|
|
82
86
|
import { assertRecipeBundleSafe, assertPortableWorkflowDefinition, assertWorkflowGraphManifest, assertWorkflowManifest, buildRecipeManifest, compileWorkflowDefinition, isAnyWorkflowManifest, isRecipeManifest, isWorkflowDefinition, isWorkflowGraphManifest, isWorkflowManifest, hashPortableWorkflowGraphManifest, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "@oxygen/workflows";
|
|
83
87
|
import { Command, CommanderError, Option } from "commander";
|
|
84
88
|
import { execFileSync } from "node:child_process";
|
|
@@ -435,7 +439,7 @@ const RECIPE_ESBUILD_NODE_PATHS = [
|
|
|
435
439
|
resolve(CLI_MODULE_DIR, "../../../node_modules"),
|
|
436
440
|
];
|
|
437
441
|
function writeJson(value) {
|
|
438
|
-
process.stdout.write(
|
|
442
|
+
process.stdout.write(serializeCliJson(value, process.argv.includes("--pretty")));
|
|
439
443
|
}
|
|
440
444
|
function linkBar(coverage, width = 36) {
|
|
441
445
|
const filled = Math.max(0, Math.min(width, Math.round(coverage * width)));
|
|
@@ -3668,6 +3672,7 @@ export function createProgram() {
|
|
|
3668
3672
|
.name(binaryName)
|
|
3669
3673
|
.description("Revenue infrastructure for B2B startups — agent-operated GTM: tables, enrichment, sequences, workflows, CRM, knowledge. Everything here also runs in the web app and over MCP; every state-changing action returns a web_url deep-link.")
|
|
3670
3674
|
.version(OXYGEN_VERSION)
|
|
3675
|
+
.option("--pretty", "Indent JSON for human reading; omit for compact agent output.")
|
|
3671
3676
|
.option("--profile <name>", "Use a stored CLI profile for this command.")
|
|
3672
3677
|
.option("--org <organization>", "Use an organization id, Clerk org id, or slug for this command.");
|
|
3673
3678
|
program.hook("preAction", (_root, selected) => {
|
|
@@ -7236,11 +7241,12 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7236
7241
|
});
|
|
7237
7242
|
}))
|
|
7238
7243
|
.addCommand(addContractFlags(new Command("query"), TABLE_QUERY_GRID_CONTRACT, {
|
|
7239
|
-
format: { valueName: "<format>", help: "Grid cell previews/state: default 50, cap 200, optional stats/fill/window. Omit for raw filters, fields, search, sorts or offsets." },
|
|
7244
|
+
format: { valueName: "<format>", help: "Grid cell previews/state: default 50, cap 200, optional stats/fill/window. Omit for raw filters, fields, search, sorts or offsets. JSON cells are null; their digests live in __oxygen_grid_cell_previews. Use tables rows inspect --projection value for complete values." },
|
|
7245
|
+
max_chars: { help: "Maximum compact JSON envelope characters for raw or grid reads (1,000-100,000). Raw defaults to 12,000." },
|
|
7240
7246
|
})
|
|
7241
7247
|
.description("Read raw Table rows or --format grid cell previews/state with grid.nextCursor, in a JSON envelope. Use tables preview --summary-only for fill rates; tables export <table> --format table renders raw rows. Grid supports stats/fill/window options and refuses raw selectors.")
|
|
7242
7248
|
.argument("<table>", "Table id or slug.")
|
|
7243
|
-
.option("--limit <n>", "Raw: default
|
|
7249
|
+
.option("--limit <n>", "Raw: default 20, native cap 2000; --full restores default 100. Grid: default 50, cap 200.")
|
|
7244
7250
|
.option("--cursor <cursor>", "Pagination cursor returned by a previous query. Walks forward one page at a time; mutually exclusive with --offset.")
|
|
7245
7251
|
.option("--offset <n>", "Skip this many rows and start there — jump straight to a row without paging to it. Mutually exclusive with --cursor.")
|
|
7246
7252
|
.option("--fields <columns>", "Comma-separated column keys or ids to include.")
|
|
@@ -7250,14 +7256,21 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7250
7256
|
.option("--formula-values <mode>", "Formula filters are refused by default because displayed formulas are evaluated live. Refresh the formula's stored cells with `columns run <table> <column> --force` (0 credits), then pass 'materialized'; filtering uses that stored snapshot and returns a freshness note. A fill-empty field a bundle adds (definition fill: \"empty\", such as Headcount or Job title) is stored, not live: its cell is the value, so 'materialized' filters it exactly.")
|
|
7251
7257
|
.option("--sort-json <json>", "Ordered sort rules, e.g. '[{\"columnKey\":\"_created_at\",\"direction\":\"desc\"}]'. Earlier rules dominate. Mutually exclusive with --filter-json.")
|
|
7252
7258
|
.option("--no-system-fields", "Omit _row_id, _created_at, and _updated_at from returned rows (included by default).")
|
|
7259
|
+
.option("--full", "Read legacy full rows without the default character limit.")
|
|
7260
|
+
.option("--output <path>", "Write full JSON for this page to a new local file; print a small receipt.")
|
|
7253
7261
|
.option("--json", "Print a JSON envelope.")
|
|
7254
7262
|
.action(async (table, options) => {
|
|
7255
|
-
await handleAsyncAction("tables query", options, () => {
|
|
7263
|
+
await handleAsyncAction("tables query", options, async () => {
|
|
7256
7264
|
const gridArgs = readContractFlags(options, TABLE_QUERY_GRID_CONTRACT);
|
|
7257
|
-
if (gridArgs.format !== "grid" && Object.keys(gridArgs).
|
|
7265
|
+
if (gridArgs.format !== "grid" && Object.keys(gridArgs).some((key) => key !== "max_chars")) {
|
|
7258
7266
|
throw new OxygenError("invalid_request", "Grid options require --format grid.", { exitCode: 1 });
|
|
7259
7267
|
}
|
|
7260
|
-
const
|
|
7268
|
+
const full = options.full || Boolean(options.output);
|
|
7269
|
+
if (full && options.format === "grid")
|
|
7270
|
+
throw new OxygenError("invalid_request", "--full/--output require raw rows; omit --format grid.", { exitCode: 2 });
|
|
7271
|
+
const limit = readPositiveInt(options.limit) ?? (options.format !== "grid" && !full ? TABLE_QUERY_CLI_DEFAULT_LIMIT : undefined);
|
|
7272
|
+
if (options.format !== "grid" && !full && gridArgs.max_chars === undefined)
|
|
7273
|
+
gridArgs.max_chars = TABLE_QUERY_CLI_DEFAULT_MAX_CHARS;
|
|
7261
7274
|
const filters = readFilterJsonOption(options.filterJson);
|
|
7262
7275
|
const filterTree = readJsonObjectOption(options.filterTreeJson);
|
|
7263
7276
|
const formulaValues = readFormulaValuesOption(options.formulaValues);
|
|
@@ -7276,7 +7289,7 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7276
7289
|
readOption(options.search) || formulaValues || sorts?.length || (offset !== undefined && offset > 0))) {
|
|
7277
7290
|
throw new OxygenError("invalid_request", '--format grid takes only table, --limit, --cursor and optional --include-stats/--include-fill; omit --format for filters, fields, search, sorts or offsets.', { exitCode: 1 });
|
|
7278
7291
|
}
|
|
7279
|
-
|
|
7292
|
+
const data = await requestOxygen("/api/cli/tables/query", {
|
|
7280
7293
|
method: "POST",
|
|
7281
7294
|
body: {
|
|
7282
7295
|
table,
|
|
@@ -7293,6 +7306,17 @@ fixed scan; watermark_cursor starts a later bounded replay. Consumers must retai
|
|
|
7293
7306
|
...(options.systemFields === false ? { include_system_fields: false } : {}),
|
|
7294
7307
|
},
|
|
7295
7308
|
});
|
|
7309
|
+
// Enforce the window even against an older compatible serving API.
|
|
7310
|
+
if (options.format !== "grid" && !tableQueryRawResultFits(data, typeof gridArgs.max_chars === "number" ? gridArgs.max_chars : undefined)) {
|
|
7311
|
+
const firstRow = Array.isArray(data.rows) ? data.rows[0] : undefined;
|
|
7312
|
+
throw new OxygenError("table_query_result_too_large", "Table rows exceed the requested result window.", {
|
|
7313
|
+
details: { max_chars: gridArgs.max_chars, ...tableQueryRawRecovery(table, typeof firstRow?._row_id === "string" ? firstRow._row_id : undefined) }, exitCode: 2,
|
|
7314
|
+
});
|
|
7315
|
+
}
|
|
7316
|
+
if (!options.output)
|
|
7317
|
+
return data;
|
|
7318
|
+
const output = await writeExclusiveJson(options.output, success("tables query", data), process.argv.includes("--pretty"));
|
|
7319
|
+
return { output, returned_count: data.returned_count ?? data.returnedCount, total_count: data.total_count ?? data.totalCount, hasMore: data.hasMore, nextCursor: data.nextCursor, web_url: data.web_url };
|
|
7296
7320
|
});
|
|
7297
7321
|
}))
|
|
7298
7322
|
.addCommand(new Command("preview")
|
|
@@ -10656,11 +10680,17 @@ Examples:
|
|
|
10656
10680
|
.addCommand(new Command("functions")
|
|
10657
10681
|
.description("List every formula function (name, signature, examples), the operator grammar, and the expression_notes that a signature cannot teach (bare column keys, string escapes, no regex flags, read-time evaluation). The same language powers formula columns and --run-condition gates.")
|
|
10658
10682
|
.option("--category <category>", "Filter by category: logic, string, number, date, url_email, array, json, null_handling, cross_row.")
|
|
10683
|
+
.option("--query <text>", "Find functions by name, signature or description.")
|
|
10659
10684
|
.option("--json", "Print a JSON envelope.")
|
|
10660
10685
|
.action(async (options) => {
|
|
10661
10686
|
await handleAsyncAction("formulas functions", options, () => {
|
|
10662
10687
|
const category = readOption(options.category);
|
|
10663
|
-
const
|
|
10688
|
+
const params = new URLSearchParams();
|
|
10689
|
+
if (category)
|
|
10690
|
+
params.set("category", category);
|
|
10691
|
+
if (readOption(options.query))
|
|
10692
|
+
params.set("query", readOption(options.query));
|
|
10693
|
+
const query = params.size ? `?${params.toString()}` : "";
|
|
10664
10694
|
return requestOxygen(`/api/cli/tables/formulas/functions${query}`, { method: "GET" });
|
|
10665
10695
|
});
|
|
10666
10696
|
}))
|
|
@@ -10739,9 +10769,11 @@ Examples:
|
|
|
10739
10769
|
.addCommand(new Command("get")
|
|
10740
10770
|
.description("Get one durable table action run. For a Function run, selection.rowIds are rows of the Function's execution table, not the calling table; list the calling table's rows it wrote with `tables query <caller> --filter-tree-json` on the Function column's \"action_run_id\" path.")
|
|
10741
10771
|
.argument("<run_id>", "Table action run UUID or parent workspace run UUID.")
|
|
10772
|
+
.option("--full", "Read the full run, including stored inputs and metadata.")
|
|
10742
10773
|
.option("--json", "Print a JSON envelope.")
|
|
10743
10774
|
.action(async (runId, options) => {
|
|
10744
|
-
await handleAsyncAction("table-runs get", options, () => requestOxygen(`/api/cli/table-action-runs/${encodeURIComponent(runId)}`)
|
|
10775
|
+
await handleAsyncAction("table-runs get", options, () => requestOxygen(`/api/cli/table-action-runs/${encodeURIComponent(runId)}?view=${options.full ? "full" : "summary"}`)
|
|
10776
|
+
.then((run) => options.full ? run : tableRunSummary(run)));
|
|
10745
10777
|
}))
|
|
10746
10778
|
.addCommand(new Command("items")
|
|
10747
10779
|
.description("List row items for a durable table action run. For a Function run, list the calling table's rows it wrote with `tables query <caller> --filter-tree-json` on the Function column's \"action_run_id\" path instead of joining items.")
|
|
@@ -10766,6 +10798,7 @@ Examples:
|
|
|
10766
10798
|
.argument("<run_id>", "Table action run UUID or parent workspace run UUID.")
|
|
10767
10799
|
.option("--timeout-seconds <n>", "Maximum time to wait. Defaults to 600.")
|
|
10768
10800
|
.option("--interval-seconds <n>", "Polling interval. Defaults to 5.")
|
|
10801
|
+
.option("--full", "Return the full completed run instead of a progress summary.")
|
|
10769
10802
|
.option("--json", "Print a JSON envelope.")
|
|
10770
10803
|
.action(async (runId, options) => {
|
|
10771
10804
|
await handleAsyncAction("table-runs wait", options, () => waitForTableActionRun(runId, options));
|
|
@@ -12709,6 +12742,32 @@ Examples:
|
|
|
12709
12742
|
program
|
|
12710
12743
|
.command("copilot")
|
|
12711
12744
|
.description("Workspace Copilot: an attended workspace agent you drive turn by turn from the terminal. Start a session, send it messages and watch the reply stream, approve the actions it proposes, then cancel when done. Managed inference uses workspace credits; paid or external actions keep their own approval gates. Web: /copilot.")
|
|
12745
|
+
.addCommand(new Command("dev-eval")
|
|
12746
|
+
.description("DEV staff owner only: immutable USD30 campaign and supplier ledger; classify dispatches guarded inference.")
|
|
12747
|
+
.argument("<operation>", "create, register, readiness, ledger, effects, classify, accept-pilot or end")
|
|
12748
|
+
.option("--campaign-id <uuid>")
|
|
12749
|
+
.option("--session-id <uuid>")
|
|
12750
|
+
.option("--models <ids>", "Comma-separated exact priced model IDs for create")
|
|
12751
|
+
.option("--question-set <name>", "Pinned thesis-icp-1 or title-fit-1")
|
|
12752
|
+
.option("--state-json <json>", "Classifier state from the native readout")
|
|
12753
|
+
.option("--json", "Print a JSON envelope.")
|
|
12754
|
+
.action(async (operation, options) => {
|
|
12755
|
+
await handleAsyncAction("copilot dev-eval", options, () => {
|
|
12756
|
+
if (operation === "readiness")
|
|
12757
|
+
return requestOxygen("/api/cli/copilot/dev-eval?readiness=1");
|
|
12758
|
+
if (operation === "effects")
|
|
12759
|
+
return requestOxygen(`/api/cli/copilot/dev-eval?campaign_id=${encodeURIComponent(options.campaignId ?? "")}&session_id=${encodeURIComponent(options.sessionId ?? "")}`);
|
|
12760
|
+
if (operation === "ledger")
|
|
12761
|
+
return requestOxygen(`/api/cli/copilot/dev-eval?campaign_id=${encodeURIComponent(options.campaignId ?? "")}`);
|
|
12762
|
+
if (!["create", "register", "end", "classify", "accept-pilot"].includes(operation))
|
|
12763
|
+
throw new OxygenError("invalid_input", "Unknown campaign operation.", { exitCode: 1 });
|
|
12764
|
+
return requestOxygen("/api/cli/copilot/dev-eval", { method: "POST", body: {
|
|
12765
|
+
operation, campaign_id: options.campaignId, session_id: options.sessionId,
|
|
12766
|
+
...(options.questionSet ? { question_set: options.questionSet, state: JSON.parse(options.stateJson ?? "null") } : {}),
|
|
12767
|
+
...(options.models ? { models: options.models.split(",").map(value => value.trim()) } : {}),
|
|
12768
|
+
} });
|
|
12769
|
+
});
|
|
12770
|
+
}), { hidden: true })
|
|
12712
12771
|
.addCommand(new Command("start")
|
|
12713
12772
|
.description("Start an attended Workspace Copilot session on Oxygen Balanced by default. Starting is free; the next send starts inference using workspace credits. No inference budget is required. Legacy budget flags remain accepted but do not authorize or cap spend. Paid or external actions require separate approval.")
|
|
12714
12773
|
.option("--budget-credits <number>", "Legacy compatibility only: accepted and clamped, but does not authorize or cap attended Copilot inference.")
|
|
@@ -12771,6 +12830,7 @@ Examples:
|
|
|
12771
12830
|
.argument("<sessionId>", "Copilot session id.")
|
|
12772
12831
|
.option("--after-seq <n>", "Only return events after this sequence number.")
|
|
12773
12832
|
.option("--events <n>", "Maximum events to return.")
|
|
12833
|
+
.option("--wait-seconds <n>", "Native bounded event long-poll, requires --after-seq (maximum 50 seconds).")
|
|
12774
12834
|
.option("--json", "Print a JSON envelope.")
|
|
12775
12835
|
.action(async (sessionId, options) => {
|
|
12776
12836
|
await handleAsyncAction("copilot get", options, () => {
|
|
@@ -12779,6 +12839,8 @@ Examples:
|
|
|
12779
12839
|
const eventLimit = readPositiveInt(options.events);
|
|
12780
12840
|
if (afterSeq !== undefined)
|
|
12781
12841
|
query.set("after_seq", String(afterSeq));
|
|
12842
|
+
if (options.waitSeconds !== undefined)
|
|
12843
|
+
query.set("wait_seconds", String(options.waitSeconds));
|
|
12782
12844
|
if (eventLimit !== undefined)
|
|
12783
12845
|
query.set("event_limit", String(eventLimit));
|
|
12784
12846
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
@@ -16689,10 +16751,11 @@ Examples:
|
|
|
16689
16751
|
.option("--status <status>", "Filter by enrollment status (pending, active, waiting_connection, waiting_signal, replied, completed, stopped, failed, excluded). pending = enrolled, not started (next_due not_started); waiting_connection = invite sent, not accepted yet. Status is the lead's progress, not its invite: use --invite accepted for the leads that accepted.")
|
|
16690
16752
|
.option("--step <n>", "Only leads currently on this step (0-based index, as in the sequence's steps).")
|
|
16691
16753
|
.option("--invite <state>", "Only leads in this LinkedIn invite state: accepted (invited and connected), pending (invited, not accepted yet) or not_invited (no invite: already connected before, suppressed or email-only).")
|
|
16692
|
-
.option("--view <view>", "
|
|
16754
|
+
.option("--view <view>", "compact (default) or full (every enrollment field) (one short row per lead with next_due in words and local time).")
|
|
16693
16755
|
.option("--order <order>", "created (default) or due: soonest next_due first, then waiting leads by gives_up_at, then leads with nothing due.")
|
|
16694
16756
|
.option("--limit <n>", "Maximum enrollments to return (1-500).")
|
|
16695
16757
|
.option("--offset <n>", "Skip this many enrollments; pass next_offset from the previous page to continue.")
|
|
16758
|
+
.option("--full", "Read every enrollment field (same as --view full).")
|
|
16696
16759
|
.option("--json", "Print a JSON envelope.")
|
|
16697
16760
|
.action(async (sequence, options) => {
|
|
16698
16761
|
await handleAsyncAction("sequences enrollments", options, () => {
|
|
@@ -16706,7 +16769,7 @@ Examples:
|
|
|
16706
16769
|
const invite = readOption(options.invite);
|
|
16707
16770
|
if (invite)
|
|
16708
16771
|
params.set("invite", invite);
|
|
16709
|
-
const view = readOption(options.view);
|
|
16772
|
+
const view = options.full ? "full" : readOption(options.view) ?? "compact";
|
|
16710
16773
|
if (view)
|
|
16711
16774
|
params.set("view", view);
|
|
16712
16775
|
const order = readOption(options.order);
|
|
@@ -20456,7 +20519,7 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
|
|
|
20456
20519
|
})));
|
|
20457
20520
|
program
|
|
20458
20521
|
.command("skills")
|
|
20459
|
-
.description("
|
|
20522
|
+
.description("Find and install Oxygen guides, or create and import private workspace skills with skills workspace.")
|
|
20460
20523
|
.addCommand(new Command("list")
|
|
20461
20524
|
.description("List Oxygen agent skills available from the hosted skill index.")
|
|
20462
20525
|
.option("--api-url <url>", "Oxygen app URL. Defaults to OXYGEN_API_URL or https://oxygen-agent.com.")
|
|
@@ -20508,13 +20571,14 @@ Full trigger schema: oxygen workflows schema --subject trigger --json
|
|
|
20508
20571
|
registerConnectorsCommands(program, handleAsyncAction);
|
|
20509
20572
|
registerSettingsCommands(program, handleAsyncAction);
|
|
20510
20573
|
registerCrmSupportFlags(program);
|
|
20574
|
+
registerWorkspaceSkillCommands(program, handleAsyncAction);
|
|
20511
20575
|
registerCustomerOperationCommands(program, handleAsyncAction);
|
|
20512
20576
|
registerCrmFileReadCommands(program, handleAsyncAction);
|
|
20513
20577
|
// After the register* helpers: help groups are assigned to the commands that
|
|
20514
20578
|
// exist when this runs, and a family registered later fell into commander's
|
|
20515
20579
|
// trailing default "Commands:" list (the 2026-09-30 blind agent read the first
|
|
20516
20580
|
// 80 lines of help and missed `connectors` there).
|
|
20517
|
-
applyOxygenHelp(program, binaryName);
|
|
20581
|
+
applyOxygenHelp(program, binaryName, `${defaultApiUrl().replace(/\/+$/, "")}/docs`);
|
|
20518
20582
|
// Last, so every command registered above is covered.
|
|
20519
20583
|
enableCommandSuggestions(program);
|
|
20520
20584
|
return program;
|
|
@@ -24314,12 +24378,12 @@ function buildGetSurfaceRunWaitConfig(params) {
|
|
|
24314
24378
|
defaultTimeoutSeconds: params.defaultTimeoutSeconds,
|
|
24315
24379
|
defaultIntervalSeconds: params.defaultIntervalSeconds,
|
|
24316
24380
|
retryTransientNetworkErrors: params.retryTransientNetworkErrors === true,
|
|
24317
|
-
fetchRun: (signal) => requestOxygen(`${params.endpoint}/${encodeURIComponent(params.runId)}`, signal ? { signal } : {}),
|
|
24381
|
+
fetchRun: (signal) => requestOxygen(`${params.endpoint}/${encodeURIComponent(params.runId)}${params.view ? `?view=${params.view}` : ""}`, signal ? { signal } : {}),
|
|
24318
24382
|
isTerminal: params.isTerminal,
|
|
24319
24383
|
shapeTerminal: (run, status, polls, elapsedMs) => {
|
|
24320
24384
|
const webUrl = readRecordString(run, "web_url");
|
|
24321
24385
|
return {
|
|
24322
|
-
[params.runKey]: run,
|
|
24386
|
+
[params.runKey]: params.view === "summary" ? tableRunSummary(run) : run,
|
|
24323
24387
|
[params.runIdKey]: readRecordString(run, "id") ?? params.runId,
|
|
24324
24388
|
status,
|
|
24325
24389
|
terminal: true,
|
|
@@ -24372,6 +24436,7 @@ function waitForTableActionRun(runId, options) {
|
|
|
24372
24436
|
return waitForCliRun(buildGetSurfaceRunWaitConfig({
|
|
24373
24437
|
runId,
|
|
24374
24438
|
endpoint: "/api/cli/table-action-runs",
|
|
24439
|
+
view: options.full ? "full" : "summary",
|
|
24375
24440
|
requestedTimeoutSeconds: options.timeoutSeconds,
|
|
24376
24441
|
requestedIntervalSeconds: options.intervalSeconds,
|
|
24377
24442
|
defaultTimeoutSeconds: TABLE_ACTION_RUN_WAIT_DEFAULT_TIMEOUT_SECONDS,
|
|
@@ -30623,7 +30688,7 @@ beginCliDiagnostics();
|
|
|
30623
30688
|
maybeScheduleBackgroundUpdate();
|
|
30624
30689
|
const program = createProgram();
|
|
30625
30690
|
installCommanderExitOverride(program);
|
|
30626
|
-
const argv = withoutHelpForUnknownCommand(program, process.argv);
|
|
30691
|
+
const argv = withoutHelpForUnknownCommand(program, process.argv.map((arg) => arg === "--help-full" ? "--help" : arg));
|
|
30627
30692
|
setCliDiagnosticOperation(commandNameForArgv(program, argv.slice(2)));
|
|
30628
30693
|
try {
|
|
30629
30694
|
await program.parseAsync(argv);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function serializeCliJson(value: unknown, pretty?: boolean): string;
|
|
2
|
+
/** Own only the newly created file; preserve an existing destination on every failure. */
|
|
3
|
+
export declare function writeExclusiveJson(path: string, value: unknown, pretty?: boolean): Promise<string>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { open, unlink } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
4
|
+
export function serializeCliJson(value, pretty = false) {
|
|
5
|
+
return `${JSON.stringify(value, null, pretty ? 2 : undefined)}\n`;
|
|
6
|
+
}
|
|
7
|
+
/** Own only the newly created file; preserve an existing destination on every failure. */
|
|
8
|
+
export async function writeExclusiveJson(path, value, pretty = false) {
|
|
9
|
+
const destination = resolve(path);
|
|
10
|
+
const serialized = serializeCliJson(value, pretty);
|
|
11
|
+
let handle;
|
|
12
|
+
try {
|
|
13
|
+
handle = await open(destination, "wx", 0o600);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error.code === "EEXIST") {
|
|
17
|
+
throw new OxygenError("output_exists", "Output file already exists. Choose a new path.", { exitCode: 2 });
|
|
18
|
+
}
|
|
19
|
+
throw new OxygenError("output_write_failed", "Unable to create the output file. Check the path and permissions.", { exitCode: 2 });
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await handle.writeFile(serialized, "utf8");
|
|
23
|
+
await handle.close();
|
|
24
|
+
return destination;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
await handle.close().catch(() => undefined);
|
|
28
|
+
const removed = await unlink(destination).then(() => true, () => false);
|
|
29
|
+
throw new OxygenError("output_write_failed", removed
|
|
30
|
+
? "Unable to write the output file. The partial file was removed."
|
|
31
|
+
: "Unable to write or remove the partial output file. Check the destination before retrying.", { exitCode: 2 });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type WorkspaceSkillFile } from "@oxygen/shared/workspace-skills";
|
|
3
|
+
/** Never follow symlinks or read binary files out of a local skill folder. */
|
|
4
|
+
export declare function readWorkspaceSkillDirectory(directory: string): Promise<WorkspaceSkillFile[]>;
|
|
5
|
+
type Handle = (command: string, options: {
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}, action: () => Promise<unknown>) => Promise<void>;
|
|
8
|
+
export declare function registerWorkspaceSkillCommands(program: Command, handle: Handle): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { OxygenError } from "@oxygen/shared/cli-result";
|
|
4
|
+
import { WORKSPACE_SKILL_OPERATIONS } from "@oxygen/shared/contracts/workspace-skills";
|
|
5
|
+
import { authorWorkspaceSkill, parseWorkspaceSkillBundle, WORKSPACE_SKILL_MAX_BYTES, WORKSPACE_SKILL_MAX_FILES, WORKSPACE_SKILL_MAX_FILE_BYTES, } from "@oxygen/shared/workspace-skills";
|
|
6
|
+
import { requestOxygen } from "./http-client.js";
|
|
7
|
+
/** Never follow symlinks or read binary files out of a local skill folder. */
|
|
8
|
+
export async function readWorkspaceSkillDirectory(directory) {
|
|
9
|
+
const root = resolve(directory);
|
|
10
|
+
const files = [];
|
|
11
|
+
let bytes = 0;
|
|
12
|
+
const visit = async (relative, depth) => {
|
|
13
|
+
if (depth > 15)
|
|
14
|
+
throw new OxygenError("invalid_workspace_skill", "Skill directory nesting exceeds 15 levels.", { exitCode: 2 });
|
|
15
|
+
const path = join(root, relative);
|
|
16
|
+
const stat = await lstat(path);
|
|
17
|
+
if (stat.isSymbolicLink())
|
|
18
|
+
throw new OxygenError("invalid_workspace_skill", "Skill imports do not follow symbolic links.", { exitCode: 2 });
|
|
19
|
+
if (stat.isDirectory()) {
|
|
20
|
+
for (const child of (await readdir(path)).sort()) {
|
|
21
|
+
if ([".git", "node_modules", ".DS_Store"].includes(child))
|
|
22
|
+
continue;
|
|
23
|
+
await visit(relative ? `${relative}/${child}` : child, depth + 1);
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!stat.isFile() ||
|
|
28
|
+
stat.size > WORKSPACE_SKILL_MAX_FILE_BYTES ||
|
|
29
|
+
files.length >= WORKSPACE_SKILL_MAX_FILES ||
|
|
30
|
+
bytes + stat.size > WORKSPACE_SKILL_MAX_BYTES) {
|
|
31
|
+
throw new OxygenError("invalid_workspace_skill", "Import supports at most 50 text files, 120 KB per file and 1 MB total.", { exitCode: 2 });
|
|
32
|
+
}
|
|
33
|
+
const buffer = await readFile(path);
|
|
34
|
+
bytes += buffer.byteLength;
|
|
35
|
+
let content;
|
|
36
|
+
try {
|
|
37
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(buffer);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new OxygenError("invalid_workspace_skill", `File ${relative} is not UTF-8 text.`, { exitCode: 2 });
|
|
41
|
+
}
|
|
42
|
+
files.push({ path: relative, content });
|
|
43
|
+
};
|
|
44
|
+
await visit("", 0);
|
|
45
|
+
return files;
|
|
46
|
+
}
|
|
47
|
+
export function registerWorkspaceSkillCommands(program, handle) {
|
|
48
|
+
const skills = program.commands.find((command) => command.name() === "skills");
|
|
49
|
+
const workspace = skills
|
|
50
|
+
.command("workspace")
|
|
51
|
+
.description("Create, upload, find and edit skills private to this workspace's members.");
|
|
52
|
+
workspace
|
|
53
|
+
.command("create")
|
|
54
|
+
.description(WORKSPACE_SKILL_OPERATIONS.find((operation) => operation.id === "workspace_skills.create").cli.summary)
|
|
55
|
+
.requiredOption("--name <name>", "Lowercase-hyphen name; stored as workspace-<name>.")
|
|
56
|
+
.requiredOption("--description <text>", "When to use this skill.")
|
|
57
|
+
.requiredOption("--instructions <text>", "Instructions to save.")
|
|
58
|
+
.option("--json", "Print a JSON envelope.")
|
|
59
|
+
.action((options, command) => handle("skills workspace create", { json: command.optsWithGlobals().json === true }, async () => {
|
|
60
|
+
authorWorkspaceSkill(options);
|
|
61
|
+
return requestOxygen("/api/cli/skills/workspace/create", {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body: {
|
|
64
|
+
name: options.name,
|
|
65
|
+
description: options.description,
|
|
66
|
+
instructions: options.instructions,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}));
|
|
70
|
+
workspace
|
|
71
|
+
.command("import")
|
|
72
|
+
.description(WORKSPACE_SKILL_OPERATIONS.find((operation) => operation.id === "workspace_skills.import").cli.summary)
|
|
73
|
+
.option("--file <path>", "Local SKILL.md file.")
|
|
74
|
+
.option("--directory <path>", "Local skill directory including SKILL.md and supporting text files.")
|
|
75
|
+
.option("--url <url>", "Import a public HTTPS SKILL.md URL. Supporting files require --directory or --files-json.")
|
|
76
|
+
.option("--files-json <json>", "Inline array of {path, content}; alternative to --file or --directory.")
|
|
77
|
+
.option("--name <name>", "Destination name; otherwise use SKILL.md frontmatter name.")
|
|
78
|
+
.option("--source-url <url>", "HTTPS source provenance; content must already be downloaded.")
|
|
79
|
+
.option("--json", "Print a JSON envelope.")
|
|
80
|
+
.action((options, command) => handle("skills workspace import", { json: command.optsWithGlobals().json === true }, async () => {
|
|
81
|
+
if ([
|
|
82
|
+
options.file,
|
|
83
|
+
options.directory,
|
|
84
|
+
options.url,
|
|
85
|
+
options.filesJson,
|
|
86
|
+
].filter((value) => value !== undefined).length !== 1)
|
|
87
|
+
throw new OxygenError("invalid_workspace_skill", "Choose exactly one of --file, --directory, --url or --files-json.", { exitCode: 2 });
|
|
88
|
+
if (options.url !== undefined) {
|
|
89
|
+
if (options.sourceUrl !== undefined)
|
|
90
|
+
throw new OxygenError("invalid_workspace_skill", "--source-url is for uploaded files. A URL import records its own source.", { exitCode: 2 });
|
|
91
|
+
return requestOxygen("/api/cli/skills/workspace/import", {
|
|
92
|
+
method: "POST",
|
|
93
|
+
body: {
|
|
94
|
+
url: options.url,
|
|
95
|
+
...(options.name ? { name: options.name } : {}),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
let files;
|
|
100
|
+
if (options.directory)
|
|
101
|
+
files = await readWorkspaceSkillDirectory(options.directory);
|
|
102
|
+
else if (options.file) {
|
|
103
|
+
const stat = await lstat(options.file);
|
|
104
|
+
if (!stat.isFile() || stat.size > WORKSPACE_SKILL_MAX_FILE_BYTES)
|
|
105
|
+
throw new OxygenError("invalid_workspace_skill", "Provide a regular SKILL.md text file of at most 120 KB.", { exitCode: 2 });
|
|
106
|
+
files = [
|
|
107
|
+
{
|
|
108
|
+
path: "SKILL.md",
|
|
109
|
+
content: new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(await readFile(options.file)),
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
try {
|
|
115
|
+
files = JSON.parse(options.filesJson);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
throw new OxygenError("invalid_workspace_skill", "--files-json must be a JSON array of {path, content}.", { exitCode: 2 });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const bundle = parseWorkspaceSkillBundle({
|
|
122
|
+
files,
|
|
123
|
+
...(options.name ? { name: options.name } : {}),
|
|
124
|
+
...(options.sourceUrl ? { source_url: options.sourceUrl } : {}),
|
|
125
|
+
});
|
|
126
|
+
return requestOxygen("/api/cli/skills/workspace/import", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
body: {
|
|
129
|
+
name: bundle.name,
|
|
130
|
+
files: bundle.files,
|
|
131
|
+
...(bundle.source_url ? { source_url: bundle.source_url } : {}),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
@@ -1877,7 +1877,12 @@ function isSubscriptionRetentionIntent(query) {
|
|
|
1877
1877
|
return /\b(retention offer|win[ -]?back discount|cancellation (?:reason|feedback)|written cancellation)\b/.test(query)
|
|
1878
1878
|
|| /\b(stay|cancel|cancelling)\b/.test(query) && /\b(discount|next month|subscription reason)\b/.test(query);
|
|
1879
1879
|
}
|
|
1880
|
+
function isWorkspaceSkillIntent(query) {
|
|
1881
|
+
return /\bskills?\b/.test(query) && /\b(create|author|write|upload|import|edit|update|archive|private|workspace|custom|own|reusable)\b/.test(query);
|
|
1882
|
+
}
|
|
1880
1883
|
function explicitCapabilityIntent(query) {
|
|
1884
|
+
if (isWorkspaceSkillIntent(query))
|
|
1885
|
+
return ROUTE_BY_ID.get("knowledge-graph") ?? null;
|
|
1881
1886
|
// Importing an AI answer is GEO evidence, rather than a Table file import.
|
|
1882
1887
|
if (/\b(?:geo|aeo|ai (?:(?:search|answer) )?visibility|consumer ai answers?|answer engine optimization|generative engine optimization)\b/.test(query))
|
|
1883
1888
|
return ROUTE_BY_ID.get("geo") ?? null;
|
|
@@ -2207,6 +2212,10 @@ function isPublishingPostIntent(query) {
|
|
|
2207
2212
|
&& !(/\b(find|search|scrape|research)\b/.test(query) && /\b(about|mention|containing)\b/.test(query));
|
|
2208
2213
|
}
|
|
2209
2214
|
function recommendationsFor(card, query) {
|
|
2215
|
+
if (card.id === "knowledge-graph" && isWorkspaceSkillIntent(query)) {
|
|
2216
|
+
return { tools: ["oxygen_workspace_skills_list", "oxygen_workspace_skills_create", "oxygen_workspace_skills_import", "oxygen_workspace_skills_get", "oxygen_workspace_skills_update", "oxygen_workspace_skills_archive"],
|
|
2217
|
+
commands: ["skills workspace list", "skills workspace create", "skills workspace import", "skills workspace get", "skills workspace update", "skills workspace archive"] };
|
|
2218
|
+
}
|
|
2210
2219
|
const contactFind = card.id === "sourcing-and-provider-tools" ? oneShotContactFindingRecommendation(query) : null;
|
|
2211
2220
|
if (contactFind)
|
|
2212
2221
|
return contactFind;
|
|
@@ -218,7 +218,7 @@ export function columnDecisionOutputSchema(decision) {
|
|
|
218
218
|
confidence: { type: "number", description: decision.mode === "score"
|
|
219
219
|
? "System One confidence, 0 to 1. It is separate from level probabilities, not their top share or the gap between the top two."
|
|
220
220
|
: "How sure the classifier is, 0 to 1." },
|
|
221
|
-
uncertain: { type: "boolean", description: "True when confidence is below the
|
|
221
|
+
uncertain: { type: "boolean", description: "True when confidence is below the floor, selected evidence is missing or shortened, or required grounding is unusable." },
|
|
222
222
|
reason: { type: "string", description: decision.mode === "score"
|
|
223
223
|
? "Readable level, confidence, and top probabilities; it does not explain the row evidence."
|
|
224
224
|
: "The answer and its confidence in words." },
|
|
@@ -13,6 +13,7 @@ import { TABLE_FEED_UPDATE_OPERATION } from "./table-feed-update.js";
|
|
|
13
13
|
import { GEO_OPERATIONS } from "./geo.js";
|
|
14
14
|
import { CUSTOMER_OPERATIONS } from "./customer-operations.js";
|
|
15
15
|
import { AGENT_SESSION_OPERATIONS } from "./agent-sessions.js";
|
|
16
|
+
import { WORKSPACE_SKILL_OPERATIONS } from "./workspace-skills.js";
|
|
16
17
|
/** The route with its `{param}` segments filled in, each URL-encoded. */
|
|
17
18
|
export function operationPath(spec, params) {
|
|
18
19
|
return spec.route.replace(/\{(\w+)\}/g, (_match, name) => {
|
|
@@ -82,6 +83,6 @@ export function operationFormData(spec, body) {
|
|
|
82
83
|
}
|
|
83
84
|
return form;
|
|
84
85
|
}
|
|
85
|
-
export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS];
|
|
86
|
-
export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
|
|
87
|
-
export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
|
|
86
|
+
export const OPERATION_SPECS = [SENDER_LIMITS_SET_OPERATION, CONNECTOR_UPDATE_OPERATION, CONNECTOR_EVENTS_REVOKE_OPERATION, WORKSPACE_PAGES_GET_OPERATION, WORKSPACE_PAGES_UPDATE_OPERATION, TABLE_FEED_UPDATE_OPERATION, ...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS];
|
|
87
|
+
export const CLI_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.cli !== false);
|
|
88
|
+
export const MCP_OPERATIONS = [...CUSTOMER_OPERATIONS, ...GEO_OPERATIONS, ...BROWSER_OPERATIONS, ...AGENT_SESSION_OPERATIONS, ...WORKSPACE_SKILL_OPERATIONS].filter((spec) => spec.generated?.mcp !== false);
|
|
@@ -17,7 +17,7 @@ export declare const TABLE_QUERY_GRID_CONTRACT: import("./schema.js").ContractOb
|
|
|
17
17
|
}, never>;
|
|
18
18
|
export declare const TABLE_QUERY_GRID_NATIVE_WINDOW = 10000;
|
|
19
19
|
export declare const TABLE_QUERY_GRID_BUILTIN_WINDOW = 4000;
|
|
20
|
-
export declare const TABLE_QUERY_GRID_COPILOT_WINDOW =
|
|
20
|
+
export declare const TABLE_QUERY_GRID_COPILOT_WINDOW = 50000;
|
|
21
21
|
/** The window a built-in host reads tables in; other clients have none. */
|
|
22
22
|
export declare function tableQueryBuiltinWindow(surface: string | null | undefined): number | undefined;
|
|
23
23
|
/** Measure the complete native receipt and the escaped built-in host receipt. */
|