@fieldwork-ai/cli 0.19.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,11 +16,11 @@ Inside a Fieldwork compute container the CLI is preinstalled and pre-authenticat
16
16
  Every tool your account can use in chat (platform reads plus your org's connected integrations) is invocable by name. Arguments are one JSON object:
17
17
 
18
18
  ```bash
19
- fieldwork tool describe kb_search # description + input schema
20
- fieldwork tool run kb_search --args '{"query": "replenishment thresholds"}'
19
+ fieldwork tool describe kb_grep # description + input schema
20
+ fieldwork tool run kb_grep --args '{"pattern": "replenishment|reorder point", "-i": true}'
21
21
  echo '{"database": "Metrics DB", "sql": "select count(*) from orders"}' \
22
22
  | fieldwork tool run db_query --args - # stdin for large payloads
23
- fieldwork tool run kb_search --args '{"query": "x"}' --arg query=override
23
+ fieldwork tool run kb_grep --args '{"pattern": "x"}' --arg pattern=override
24
24
  ```
25
25
 
26
26
  `--arg key=value` overlays a string field; `--arg key:=json` parses the value as JSON. Tool-level errors (strings starting `Error:`) are data and exit 0; transport and auth failures use the exit codes below.
@@ -65,6 +65,3 @@ Source stays in the private app repository under `cli/`. Run `pnpm install --fro
65
65
 
66
66
  The terminal chat client is the separate public `@fieldwork-ai/fieldwork-code` package (`fwcode`).
67
67
 
68
- ## Graph authoring diagnostics
69
-
70
- Use `fieldwork tool run automations_graph_examples --json` for executable examples and the coordinated code/graph publication format. `automations_graph_validate` takes the publication input without committing it. `automations_graph_test` uses isolated editable-table fixtures and captured notifications; it does not execute authored code or send email. `automations_graph_wait` returns bounded, compact progress, and `automations_graph_trace` accepts `detail: "full"` when saved payloads are needed. Contract failures include source/destination paths, required coordinated changes and retryability on stderr with a nonzero exit code.
@@ -155,12 +155,12 @@ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
155
155
  * a global system, its id for an org-authored one. Mirrors `connectorAddress`
156
156
  * in the app; the CLI is its own package and shares no code with it. */
157
157
  function dbAddressOf(c) {
158
- return c.definition_is_global ? c.definition_key : c.connector_definition_id;
158
+ return c.definition_is_global ? c.definition_key : c.connector_id;
159
159
  }
160
160
  async function resolveDbConnector(database) {
161
161
  const body = await apiJson(`/api/organizations/${requireOrgId()}/db-connectors`);
162
162
  const found = UUID_RE.test(database)
163
- ? body.connectors.find((c) => c.connector_definition_id === database)
163
+ ? body.connectors.find((c) => c.connector_id === database)
164
164
  : (body.connectors.find((c) => c.definition_key === database) ??
165
165
  body.connectors.find((c) => c.name === database) ??
166
166
  body.connectors.find((c) => c.display_name === database));
@@ -71,26 +71,6 @@ async function followRun(automationId, runId, intervalSeconds, json) {
71
71
  }
72
72
  }
73
73
  export function registerAutomationCommands(program) {
74
- const graph = program
75
- .command("automation-graph", { hidden: true })
76
- .description("Publish and inspect automation graphs");
77
- graph.command("get <automation-id>").action(async (id) => printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/${encodeURIComponent(id)}/graph`), true));
78
- graph.command("trace <automation-id> <run-id>").action(async (id, runId) => printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/${encodeURIComponent(id)}/runs/${encodeURIComponent(runId)}/steps`), true));
79
- graph.command("configure <automation-id>").requiredOption("--file <path>").action(async (id, options) => {
80
- const body = await readFile(options.file, "utf8");
81
- await confirm("Apply these triggers and enabled state?", getContext().yes);
82
- printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/${encodeURIComponent(id)}/graph`, { method: "PATCH", body }), true);
83
- });
84
- graph.command("control <automation-id> <run-id>").requiredOption("--file <path>", "Cancel, retry, or confirmed reconciliation").action(async (id, runId, options) => {
85
- const body = await readFile(options.file, "utf8");
86
- await confirm("Apply this run control? Confirm external results before reconciliation.", getContext().yes);
87
- printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/${encodeURIComponent(id)}/runs/${encodeURIComponent(runId)}/steps`, { method: "POST", body }), true);
88
- });
89
- graph.command("publish").requiredOption("--file <path>").action(async (options) => {
90
- const body = await readFile(options.file, "utf8");
91
- await confirm("Publish this graph and delegate its listed resource access?", getContext().yes);
92
- printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/graphs`, { method: "POST", body }), true);
93
- });
94
74
  const automations = program
95
75
  .command("automations")
96
76
  .description("Create, manage and inspect automations");
@@ -101,7 +81,7 @@ export function registerAutomationCommands(program) {
101
81
  });
102
82
  printData(response.result, true);
103
83
  };
104
- for (const operation of ["publish", "validate", "test", "update"]) {
84
+ for (const operation of ["publish", "update"]) {
105
85
  automations
106
86
  .command(operation)
107
87
  .description(`${operation} an automation`)
@@ -116,17 +96,13 @@ export function registerAutomationCommands(program) {
116
96
  automations
117
97
  .command("run <name>")
118
98
  .description("Start a real automation run")
119
- .option("--input <path>", "File containing the run input")
120
- .action(async (name, options) => {
121
- const input = options.input
122
- ? JSON.parse(await readFile(options.input, "utf8"))
123
- : undefined;
99
+ .action(async (name) => {
124
100
  await confirm(`Run "${name}" now?`, getContext().yes);
125
- await callTool("automations_run", { name, input });
101
+ await callTool("automations_run", { name });
126
102
  });
127
103
  automations
128
104
  .command("examples")
129
- .description("Working automation definitions and tests")
105
+ .description("A working code automation")
130
106
  .action(() => callTool("automations_examples", {}));
131
107
  for (const operation of ["get-run", "wait"]) {
132
108
  automations
@@ -138,16 +114,6 @@ export function registerAutomationCommands(program) {
138
114
  ...(operation === "get-run" ? { detail: "full" } : {}),
139
115
  }));
140
116
  }
141
- automations
142
- .command("control <name> <run-id>")
143
- .description("Cancel, retry, or reconcile a run")
144
- .requiredOption("--file <path>")
145
- .action(async (name, runId, options) => {
146
- const found = await resolveAutomation(name);
147
- const body = await readFile(options.file, "utf8");
148
- await confirm("Apply this run control? Verify external results before reconciliation.", getContext().yes);
149
- printData(await apiJson(`/api/organizations/${requireOrgId()}/automations/${found.automation_id}/runs/${encodeURIComponent(runId)}/steps`, { method: "POST", body }), true);
150
- });
151
117
  automations
152
118
  .command("list")
153
119
  .description("List the automations you can see")
@@ -4,7 +4,7 @@ import { readBundle, writeBundle } from "../bundle.js";
4
4
  import { apiJson, getContext, requireOrgId } from "../http.js";
5
5
  import { EXIT, confirm, fail, note, printData } from "../output.js";
6
6
  async function resolveDefinition(key) {
7
- const body = await apiJson(`/api/organizations/${requireOrgId()}/connector-definitions?include_drafts=true`);
7
+ const body = await apiJson(`/api/organizations/${requireOrgId()}/connectors/library?include_drafts=true`);
8
8
  const matches = (body.definitions ?? []).filter((d) => d.key === key);
9
9
  const definition = matches.find((d) => d.org_private) ?? matches[0];
10
10
  if (!definition) {
@@ -95,7 +95,7 @@ export function writeSecretsFile(out, secrets, opts) {
95
95
  * a global system, its id for an org-authored one whose key is only unique
96
96
  * within the org. Mirrors `connectorAddress` in the app. */
97
97
  function addressOf(c) {
98
- return c.definition_is_global ? c.definition_key : c.connector_definition_id;
98
+ return c.definition_is_global ? c.definition_key : c.connector_id;
99
99
  }
100
100
  async function listConnectors() {
101
101
  const body = await apiJson(`/api/organizations/${requireOrgId()}/connectors`);
@@ -179,7 +179,7 @@ export function registerConnectorCommands(program) {
179
179
  if (opts.actionName)
180
180
  query.set("action_name", opts.actionName);
181
181
  const suffix = query.size ? `?${query}` : "";
182
- const body = await apiJson(`/api/organizations/${requireOrgId()}/connector-definitions/${definition.connector_definition_id}/source${suffix}`);
182
+ const body = await apiJson(`/api/organizations/${requireOrgId()}/connectors/${definition.connector_id}/source${suffix}`);
183
183
  const dir = opts.out ?? key;
184
184
  const pulled = writeBundle(dir, body.files);
185
185
  if (json) {
@@ -212,7 +212,7 @@ export function registerConnectorCommands(program) {
212
212
  fail('the tree\'s connector.json must carry a "key" field', EXIT.usage);
213
213
  }
214
214
  await confirm(`Publish ${Object.keys(files).length} files to connector "${key}"${opts.actionName ? `, action "${opts.actionName}"` : ""}?`, yes);
215
- const body = await apiJson(`/api/organizations/${requireOrgId()}/connector-definitions/publish`, {
215
+ const body = await apiJson(`/api/organizations/${requireOrgId()}/connectors/publish`, {
216
216
  method: "POST",
217
217
  body: JSON.stringify({
218
218
  message: opts.message,
@@ -226,14 +226,14 @@ export function registerConnectorCommands(program) {
226
226
  connectors.command("metadata <definition-id>")
227
227
  .description("Read an org-private connector definition's editable metadata")
228
228
  .action(async (id) => {
229
- printData(await apiJson(`/api/organizations/${requireOrgId()}/connector-definitions/${id}`), getContext().json);
229
+ printData(await apiJson(`/api/organizations/${requireOrgId()}/connectors/${id}/metadata`), getContext().json);
230
230
  });
231
231
  connectors.command("update-metadata <definition-id>")
232
232
  .description("Edit connector metadata separately from action code")
233
233
  .requiredOption("--changes <json-file>", "metadata fields to change")
234
234
  .requiredOption("--expected-updated-at <timestamp>", "exact updated_at from metadata read")
235
235
  .action(async (id, opts) => {
236
- const url = `/api/organizations/${requireOrgId()}/connector-definitions/${id}`;
236
+ const url = `/api/organizations/${requireOrgId()}/connectors/${id}/metadata`;
237
237
  const before = await apiJson(url);
238
238
  if (before.updated_at !== opts.expectedUpdatedAt)
239
239
  fail("Connector metadata changed; read it again before updating", EXIT.conflict);
@@ -1,2 +1,3 @@
1
1
  import { Command } from "commander";
2
- export declare function tablesCommand(): Command;
2
+ /** `fieldwork sheets`; also registered, hidden, under its old name `tables`. */
3
+ export declare function sheetsCommand(name?: string): Command;
@@ -2,18 +2,19 @@ import { readFile } from "node:fs/promises";
2
2
  import { Command } from "commander";
3
3
  import { apiJson, requireOrgId, getContext } from "../http.js";
4
4
  import { confirm, printData } from "../output.js";
5
- export function tablesCommand() {
6
- const command = new Command("tables").description("Fieldwork Tables and editable records");
5
+ /** `fieldwork sheets`; also registered, hidden, under its old name `tables`. */
6
+ export function sheetsCommand(name = "sheets") {
7
+ const command = new Command(name).description("Sheets and their editable records");
7
8
  const base = () => `/api/organizations/${requireOrgId()}/tables`;
8
9
  command
9
10
  .command("list")
10
11
  .action(async () => printData(await apiJson(base()), true));
11
12
  command
12
13
  .command("create")
13
- .requiredOption("--file <path>", "Table definition file")
14
+ .requiredOption("--file <path>", "Sheet definition file")
14
15
  .action(async (options) => {
15
16
  const body = await readFile(options.file, "utf8");
16
- await confirm("Create this Fieldwork Table?", getContext().yes);
17
+ await confirm("Create this sheet?", getContext().yes);
17
18
  printData(await apiJson(base(), { method: "POST", body }), true);
18
19
  });
19
20
  command
package/dist/http.js CHANGED
@@ -121,16 +121,6 @@ export async function apiJson(pathname, init = {}) {
121
121
  const { path, message } = issue;
122
122
  return typeof message === "string" ? [`${Array.isArray(path) ? path.join(".") + ": " : ""}${message}`] : [];
123
123
  }) : [];
124
- if (body?.code === "invalid_graph" && body.details && typeof body.details === "object" && !Array.isArray(body.details)) {
125
- const diagnostic = body.details;
126
- for (const key of ["action_definition_id", "automation_id", "automation_revision_id", "node_id", "source", "destination", "input", "output", "source_schema", "target_schema", "effect_changed", "credential_changed", "required_action"]) {
127
- const value = diagnostic[key];
128
- if (value !== undefined && value !== null)
129
- details.push(`${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
130
- }
131
- if (typeof body.retryable === "boolean")
132
- details.push(`retryable: ${body.retryable}`);
133
- }
134
124
  const prefix = typeof body?.tool === "string" ? `${body.tool}: ` : "";
135
125
  fail([prefix + message, ...details].join("\n"), exitCodeFor(res.status));
136
126
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { tablesCommand } from "./commands/tables.js";
2
+ import { sheetsCommand } from "./commands/tables.js";
3
3
  import { Command } from "commander";
4
4
  import { createRequire } from "node:module";
5
5
  import { initContext } from "./http.js";
@@ -41,7 +41,8 @@ registerAppCommands(program);
41
41
  registerAutomationCommands(program);
42
42
  registerTaskCommands(program);
43
43
  registerConnectorCommands(program);
44
- program.addCommand(tablesCommand());
44
+ program.addCommand(sheetsCommand());
45
+ program.addCommand(sheetsCommand("tables"), { hidden: true });
45
46
  program.parseAsync(process.argv).catch((err) => {
46
47
  process.stderr.write(`fieldwork: ${err instanceof Error ? err.message : String(err)}\n`);
47
48
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldwork-ai/cli",
3
- "version": "0.19.0",
3
+ "version": "0.23.0",
4
4
  "description": "Fieldwork asset-management CLI",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",