zinkee 0.1.44 → 0.1.46

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.
Files changed (94) hide show
  1. package/README.md +4 -0
  2. package/dist/chunk-MHEVRMDX.js +1230 -0
  3. package/dist/chunk-MHEVRMDX.js.map +1 -0
  4. package/{src/utils/examples.ts → dist/examples-67U5IRBS.js} +866 -1141
  5. package/dist/examples-67U5IRBS.js.map +1 -0
  6. package/dist/index.js +1145 -8456
  7. package/dist/index.js.map +1 -1
  8. package/package.json +6 -3
  9. package/.github/workflows/npm-publish.yml +0 -77
  10. package/.github/workflows/pr-checks.yml +0 -36
  11. package/AGENTS.md +0 -110
  12. package/docs/cli-contract.md +0 -41
  13. package/docs/npm-release.md +0 -64
  14. package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
  15. package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
  16. package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
  17. package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
  18. package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
  19. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
  20. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
  21. package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
  22. package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
  23. package/src/api/automations.ts +0 -404
  24. package/src/api/comments.ts +0 -51
  25. package/src/api/displays.ts +0 -337
  26. package/src/api/document-templates.ts +0 -110
  27. package/src/api/files.ts +0 -50
  28. package/src/api/formulas.test.ts +0 -65
  29. package/src/api/formulas.ts +0 -67
  30. package/src/api/logs.ts +0 -34
  31. package/src/api/navigation.ts +0 -70
  32. package/src/api/records.ts +0 -184
  33. package/src/api/schemas.ts +0 -148
  34. package/src/api/teamspace.ts +0 -110
  35. package/src/cli-examples.ts +0 -130
  36. package/src/cli-runner.ts +0 -95
  37. package/src/client.test.ts +0 -189
  38. package/src/client.ts +0 -269
  39. package/src/command-registry.ts +0 -882
  40. package/src/commands/automations.test.ts +0 -1030
  41. package/src/commands/automations.ts +0 -2102
  42. package/src/commands/comments.test.ts +0 -214
  43. package/src/commands/comments.ts +0 -303
  44. package/src/commands/config.test.ts +0 -81
  45. package/src/commands/config.ts +0 -150
  46. package/src/commands/displays.test.ts +0 -1105
  47. package/src/commands/displays.ts +0 -1442
  48. package/src/commands/document-templates.test.ts +0 -569
  49. package/src/commands/document-templates.ts +0 -563
  50. package/src/commands/files.test.ts +0 -284
  51. package/src/commands/files.ts +0 -280
  52. package/src/commands/formulas.test.ts +0 -194
  53. package/src/commands/formulas.ts +0 -243
  54. package/src/commands/logs.test.ts +0 -123
  55. package/src/commands/logs.ts +0 -159
  56. package/src/commands/navigation.test.ts +0 -211
  57. package/src/commands/navigation.ts +0 -348
  58. package/src/commands/profiles.test.ts +0 -191
  59. package/src/commands/profiles.ts +0 -303
  60. package/src/commands/records.test.ts +0 -860
  61. package/src/commands/records.ts +0 -883
  62. package/src/commands/schemas.test.ts +0 -1252
  63. package/src/commands/schemas.ts +0 -890
  64. package/src/commands/teamspace.test.ts +0 -229
  65. package/src/commands/teamspace.ts +0 -546
  66. package/src/completion/engine.test.ts +0 -138
  67. package/src/completion/engine.ts +0 -168
  68. package/src/completion/install.test.ts +0 -179
  69. package/src/completion/install.ts +0 -260
  70. package/src/completion/runtime.ts +0 -150
  71. package/src/completion/scripts.ts +0 -91
  72. package/src/config.test.ts +0 -362
  73. package/src/config.ts +0 -294
  74. package/src/index.test.ts +0 -217
  75. package/src/index.ts +0 -8
  76. package/src/parsers/expressions.test.ts +0 -95
  77. package/src/parsers/expressions.ts +0 -128
  78. package/src/parsers/kv.test.ts +0 -35
  79. package/src/parsers/kv.ts +0 -49
  80. package/src/parsers/selectors.test.ts +0 -23
  81. package/src/parsers/selectors.ts +0 -29
  82. package/src/program.ts +0 -64
  83. package/src/runtime-context.ts +0 -103
  84. package/src/types.ts +0 -76
  85. package/src/utils/argv-rewrite.test.ts +0 -149
  86. package/src/utils/argv-rewrite.ts +0 -269
  87. package/src/utils/errors.test.ts +0 -85
  88. package/src/utils/errors.ts +0 -191
  89. package/src/utils/examples.test.ts +0 -374
  90. package/src/utils/output.test.ts +0 -65
  91. package/src/utils/output.ts +0 -154
  92. package/src/utils/schema-fields.ts +0 -1016
  93. package/tsconfig.json +0 -20
  94. package/tsup.config.ts +0 -13
@@ -1,211 +0,0 @@
1
- import { mkdtempSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
-
5
- import { Command } from "commander";
6
- import { afterEach, describe, expect, it, vi } from "vitest";
7
-
8
- import { registerNavigationCommands } from "./navigation.js";
9
-
10
- function createTempConfig(contents: string): string {
11
- const dir = mkdtempSync(join(tmpdir(), "zinkee-navigation-"));
12
- const filePath = join(dir, "config.toml");
13
- writeFileSync(filePath, contents, "utf8");
14
- return filePath;
15
- }
16
-
17
- function createApiConfig(): string {
18
- return createTempConfig(`
19
- default_profile = "prod"
20
-
21
- [profiles.prod]
22
- api_key = "navigation-token-1234"
23
- base_url = "https://api.zinkee.test"
24
- `);
25
- }
26
-
27
- function createStdoutBuffer() {
28
- let output = "";
29
-
30
- return {
31
- stdout: {
32
- write(chunk: string) {
33
- output += chunk;
34
- return true;
35
- },
36
- },
37
- read() {
38
- return output;
39
- },
40
- };
41
- }
42
-
43
- function jsonResponse(body: unknown, status: number = 200): Response {
44
- return new Response(JSON.stringify(body), {
45
- status,
46
- headers: {
47
- "content-type": "application/json",
48
- },
49
- });
50
- }
51
-
52
- function createFetchMock() {
53
- const fetchMock = vi.fn<typeof fetch>();
54
- vi.stubGlobal("fetch", fetchMock);
55
- return fetchMock;
56
- }
57
-
58
- function buildProgram(configPath: string, stdout: { write(chunk: string): unknown }) {
59
- const program = new Command();
60
-
61
- program
62
- .name("zinkee")
63
- .enablePositionalOptions()
64
- .showHelpAfterError()
65
- .option("--profile <name>", "select a profile from config")
66
- .option("--base-url <url>", "override the configured base URL")
67
- .option("--api-key <token>", "override the configured API key")
68
- .option("--read-only", "run in read-only mode")
69
- .option("--json", "emit JSON output");
70
-
71
- registerNavigationCommands(program, { configPath, stdout });
72
-
73
- return program;
74
- }
75
-
76
- afterEach(() => {
77
- vi.unstubAllGlobals();
78
- vi.restoreAllMocks();
79
- });
80
-
81
- describe("navigation commands", () => {
82
- it("resolves exact folder names for get and rejects duplicate matches", async () => {
83
- const configPath = createApiConfig();
84
- const output = createStdoutBuffer();
85
- const fetchMock = createFetchMock();
86
- fetchMock.mockResolvedValueOnce(
87
- jsonResponse([
88
- {
89
- id: "11111111-1111-1111-1111-111111111111",
90
- name: "Finance",
91
- order: 10,
92
- resources: [],
93
- },
94
- {
95
- id: "22222222-2222-2222-2222-222222222222",
96
- name: "Marketing",
97
- order: 20,
98
- resources: [],
99
- },
100
- ]),
101
- );
102
- const program = buildProgram(configPath, output.stdout);
103
-
104
- await program.parseAsync([
105
- "node",
106
- "zinkee",
107
- "--json",
108
- "navigation",
109
- "folders",
110
- "get",
111
- "Finance",
112
- ]);
113
-
114
- expect(fetchMock.mock.calls[0]?.[0]).toBe(
115
- "https://api.zinkee.test/api/v2/navigation/folders",
116
- );
117
- expect(JSON.parse(output.read())).toEqual({
118
- data: {
119
- id: "11111111-1111-1111-1111-111111111111",
120
- name: "Finance",
121
- order: 10,
122
- resources: [],
123
- },
124
- meta: {
125
- command: "navigation folders get",
126
- configPath,
127
- },
128
- });
129
-
130
- fetchMock.mockReset();
131
- fetchMock.mockResolvedValueOnce(
132
- jsonResponse([
133
- {
134
- id: "11111111-1111-1111-1111-111111111111",
135
- name: "Finance",
136
- order: 10,
137
- resources: [],
138
- },
139
- {
140
- id: "22222222-2222-2222-2222-222222222222",
141
- name: "Finance",
142
- order: 20,
143
- resources: [],
144
- },
145
- ]),
146
- );
147
-
148
- await expect(
149
- buildProgram(configPath, output.stdout).parseAsync([
150
- "node",
151
- "zinkee",
152
- "navigation",
153
- "folders",
154
- "get",
155
- "Finance",
156
- ]),
157
- ).rejects.toThrowError('Folder "Finance" is ambiguous. Found 2 matches.');
158
- });
159
-
160
- it("shapes move payloads using the resolved folder id (schemas only, no resource type)", async () => {
161
- const configPath = createApiConfig();
162
- const output = createStdoutBuffer();
163
- const fetchMock = createFetchMock();
164
- fetchMock.mockResolvedValueOnce(
165
- jsonResponse([
166
- {
167
- id: "aaaabbbb-cccc-dddd-eeee-ffffffffffff",
168
- name: "Finance",
169
- order: 10,
170
- resources: [
171
- {
172
- id: "resource-1",
173
- name: "Invoices",
174
- type: "SCHEMA",
175
- isInSpace: false,
176
- },
177
- ],
178
- },
179
- ]),
180
- );
181
- fetchMock.mockResolvedValueOnce(jsonResponse({ resourceId: "resource-1", targetFolderId: "aaaabbbb-cccc-dddd-eeee-ffffffffffff" }));
182
- const program = buildProgram(configPath, output.stdout);
183
-
184
- await program.parseAsync([
185
- "node",
186
- "zinkee",
187
- "--json",
188
- "navigation",
189
- "resources",
190
- "move",
191
- "resource-1",
192
- "--to",
193
- "Finance",
194
- ]);
195
-
196
- expect(fetchMock.mock.calls[0]?.[0]).toBe(
197
- "https://api.zinkee.test/api/v2/navigation/folders",
198
- );
199
-
200
- const moveInit = fetchMock.mock.calls[1]?.[1];
201
-
202
- expect(fetchMock.mock.calls[1]?.[0]).toBe(
203
- "https://api.zinkee.test/api/v2/navigation/resources:move",
204
- );
205
- expect(moveInit?.method).toBe("POST");
206
- expect(JSON.parse(String(moveInit?.body))).toEqual({
207
- resourceId: "resource-1",
208
- targetFolderId: "aaaabbbb-cccc-dddd-eeee-ffffffffffff",
209
- });
210
- });
211
- });
@@ -1,348 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import type { Writable } from "node:stream";
3
-
4
- import { Command, InvalidArgumentError } from "commander";
5
-
6
- import { NavigationApi, type NavigationFolder } from "../api/navigation.js";
7
- import { ApiClient } from "../client.js";
8
- import { defaultConfigPath, readRuntimeConfig } from "../config.js";
9
- import { normalizeUuidOrSlug, type NormalizedUuidOrSlug } from "../parsers/selectors.js";
10
- import { resolveRuntimeContext } from "../runtime-context.js";
11
- import type { RuntimeOverrides } from "../types.js";
12
- import { CliError } from "../utils/errors.js";
13
- import { buildJsonSuccess, printTable } from "../utils/output.js";
14
-
15
- interface CommandIo {
16
- write(chunk: string): unknown;
17
- }
18
-
19
- export interface RegisterNavigationCommandsOptions {
20
- configPath?: string;
21
- stdout?: CommandIo;
22
- }
23
-
24
- interface GlobalOptions extends RuntimeOverrides {}
25
-
26
- interface FolderMutationOptions {
27
- name?: string;
28
- order?: string;
29
- props?: string;
30
- propsFile?: string;
31
- }
32
-
33
- function getCommandIo(stdout: CommandIo | undefined): CommandIo {
34
- return stdout ?? (process.stdout as Writable);
35
- }
36
-
37
- function writeLine(stdout: CommandIo, value: string): void {
38
- stdout.write(`${value}\n`);
39
- }
40
-
41
- function getGlobalOptions(command: Command): GlobalOptions {
42
- return command.optsWithGlobals<GlobalOptions>();
43
- }
44
-
45
- function emitJson(
46
- stdout: CommandIo,
47
- commandName: string,
48
- data: unknown,
49
- configPath: string,
50
- ): void {
51
- writeLine(stdout, JSON.stringify(buildJsonSuccess(data, { command: commandName, configPath })));
52
- }
53
-
54
- function toConfigurationError(filePath: string, error: unknown): never {
55
- if (error instanceof CliError) {
56
- throw error;
57
- }
58
-
59
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
60
- throw new CliError("configuration_error", `Config file not found at ${filePath}.`);
61
- }
62
-
63
- if (error instanceof Error) {
64
- throw new CliError("configuration_error", error.message);
65
- }
66
-
67
- throw error;
68
- }
69
-
70
- function createNavigationApi(
71
- command: Command,
72
- filePath: string,
73
- ): { api: NavigationApi; json: boolean; readOnly: boolean } {
74
- try {
75
- const config = readRuntimeConfig(filePath);
76
- const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
77
-
78
- if (!runtime.baseUrl) {
79
- throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
80
- }
81
-
82
- return {
83
- api: new NavigationApi(
84
- new ApiClient({
85
- baseUrl: runtime.baseUrl,
86
- apiKey: runtime.apiKey,
87
- }),
88
- ),
89
- json: runtime.json,
90
- readOnly: runtime.readOnly,
91
- };
92
- } catch (error) {
93
- toConfigurationError(filePath, error);
94
- }
95
- }
96
-
97
- function ensureWritable(commandName: string, readOnly: boolean, mode: "write" | "destructive"): void {
98
- if (readOnly) {
99
- throw new CliError(
100
- "command_not_allowed_in_read_only_mode",
101
- `Command "${commandName}" is not allowed in read-only mode.`,
102
- { commandMode: mode, executionMode: "read-only" },
103
- );
104
- }
105
- }
106
-
107
- function parseUuidOrSlugSelector(input: string): NormalizedUuidOrSlug {
108
- try {
109
- return normalizeUuidOrSlug(input);
110
- } catch (error) {
111
- const message = error instanceof Error ? error.message : "Invalid selector.";
112
- throw new InvalidArgumentError(message);
113
- }
114
- }
115
-
116
- function parseInteger(value: string | undefined, flagName: string): number | undefined {
117
- if (value === undefined) {
118
- return undefined;
119
- }
120
-
121
- const parsed = Number.parseInt(value, 10);
122
- if (Number.isNaN(parsed)) {
123
- throw new CliError("invalid_cli_usage", `${flagName} must be an integer.`);
124
- }
125
-
126
- return parsed;
127
- }
128
-
129
- function readJsonObject(raw?: string, rawFile?: string): Record<string, unknown> {
130
- if (raw && rawFile) {
131
- throw new CliError("invalid_cli_usage", 'Use either "--props" or "--props-file", not both.');
132
- }
133
-
134
- const source = rawFile ? readFileSync(rawFile, "utf8") : raw;
135
- if (!source) {
136
- return {};
137
- }
138
-
139
- try {
140
- const parsed = JSON.parse(source) as unknown;
141
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
142
- throw new CliError("invalid_cli_usage", "Props payload must be a JSON object.");
143
- }
144
- return parsed as Record<string, unknown>;
145
- } catch (error) {
146
- if (error instanceof CliError) {
147
- throw error;
148
- }
149
- throw new CliError("invalid_cli_usage", "Props payload is invalid JSON.");
150
- }
151
- }
152
-
153
- function buildFolderBody(options: FolderMutationOptions): Record<string, unknown> {
154
- const body: Record<string, unknown> = {};
155
-
156
- if (options.name !== undefined) {
157
- body.name = options.name;
158
- }
159
-
160
- const order = parseInteger(options.order, "--order");
161
- if (order !== undefined) {
162
- body.order = order;
163
- }
164
-
165
- const props = readJsonObject(options.props, options.propsFile);
166
- if (Object.keys(props).length > 0) {
167
- body.additionalProperties = props;
168
- }
169
-
170
- return body;
171
- }
172
-
173
- function resolveFolder(
174
- folders: readonly NavigationFolder[],
175
- selector: NormalizedUuidOrSlug,
176
- ): NavigationFolder {
177
- if (selector.kind === "uuid") {
178
- const match = folders.find((folder) => folder.id === selector.value);
179
- if (!match) {
180
- throw new CliError("invalid_cli_usage", `Folder "${selector.value}" was not found.`);
181
- }
182
- return match;
183
- }
184
-
185
- const matches = folders.filter((folder) => folder.name === selector.value);
186
- if (matches.length === 1) {
187
- return matches[0] as NavigationFolder;
188
- }
189
-
190
- if (matches.length > 1) {
191
- throw new CliError(
192
- "invalid_cli_usage",
193
- `Folder "${selector.value}" is ambiguous. Found ${matches.length} matches.`,
194
- );
195
- }
196
-
197
- throw new CliError("invalid_cli_usage", `Folder "${selector.value}" was not found.`);
198
- }
199
-
200
- function folderRows(folders: readonly NavigationFolder[]): Record<string, unknown>[] {
201
- return folders.map((folder) => ({
202
- id: folder.id,
203
- order: folder.order ?? "",
204
- name: folder.name ?? "",
205
- resourceCount: Array.isArray(folder.resources) ? folder.resources.length : "",
206
- }));
207
- }
208
-
209
- export function registerNavigationCommands(
210
- program: Command,
211
- options: RegisterNavigationCommandsOptions = {},
212
- ): void {
213
- const filePath = options.configPath ?? defaultConfigPath;
214
- const stdout = getCommandIo(options.stdout);
215
- const navigation = program.command("navigation").description("Manage workspace navigation");
216
- const folders = navigation.command("folders").description("Manage navigation folders");
217
-
218
- folders
219
- .command("list")
220
- .description("List navigation folders")
221
- .action(async function action(this: Command) {
222
- const { api, json } = createNavigationApi(this, filePath);
223
- const data = await api.listFolders();
224
-
225
- if (json) {
226
- emitJson(stdout, "navigation folders list", data, filePath);
227
- return;
228
- }
229
-
230
- printTable(folderRows(data), ["id", "order", "name", "resourceCount"], stdout);
231
- });
232
-
233
- folders
234
- .command("get")
235
- .description("Get a navigation folder by UUID or exact name")
236
- .argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
237
- .action(async function action(this: Command, folder: NormalizedUuidOrSlug) {
238
- const { api, json } = createNavigationApi(this, filePath);
239
- const data = await api.listFolders();
240
- const resolved = resolveFolder(data, folder);
241
-
242
- if (json) {
243
- emitJson(stdout, "navigation folders get", resolved, filePath);
244
- return;
245
- }
246
-
247
- printTable(folderRows([resolved]), ["id", "order", "name", "resourceCount"], stdout);
248
- });
249
-
250
- folders
251
- .command("create")
252
- .description("Create a navigation folder")
253
- .requiredOption("--name <name>", "folder name")
254
- .option("--order <n>", "display order (lower values appear first)")
255
- .option("--props <json>", "additionalProperties JSON: free-form client metadata, e.g. {\"color\":\"blue\",\"icon\":\"chart-line\"}")
256
- .option("--props-file <path>", "path to a file with the additionalProperties JSON")
257
- .action(async function action(this: Command, commandOptions: FolderMutationOptions) {
258
- const { api, json, readOnly } = createNavigationApi(this, filePath);
259
- ensureWritable("navigation folders create", readOnly, "write");
260
- const data = await api.createFolder(buildFolderBody(commandOptions));
261
-
262
- if (json) {
263
- emitJson(stdout, "navigation folders create", data, filePath);
264
- return;
265
- }
266
-
267
- writeLine(stdout, `Created navigation folder "${String(data.name ?? commandOptions.name)}".`);
268
- });
269
-
270
- folders
271
- .command("update")
272
- .description("Update a navigation folder")
273
- .argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
274
- .option("--name <name>", "folder name")
275
- .option("--order <n>", "display order (lower values appear first)")
276
- .option("--props <json>", "additionalProperties JSON: free-form client metadata, e.g. {\"color\":\"blue\",\"icon\":\"chart-line\"}")
277
- .option("--props-file <path>", "path to a file with the additionalProperties JSON")
278
- .action(async function action(this: Command, folder: NormalizedUuidOrSlug, commandOptions: FolderMutationOptions) {
279
- const { api, json, readOnly } = createNavigationApi(this, filePath);
280
- ensureWritable("navigation folders update", readOnly, "write");
281
- const allFolders = await api.listFolders();
282
- const resolved = resolveFolder(allFolders, folder);
283
- const body = buildFolderBody(commandOptions);
284
-
285
- if (Object.keys(body).length === 0) {
286
- throw new CliError("invalid_cli_usage", "Nothing to update.");
287
- }
288
-
289
- const data = await api.updateFolder({ kind: "uuid", value: resolved.id }, body);
290
-
291
- if (json) {
292
- emitJson(stdout, "navigation folders update", data, filePath);
293
- return;
294
- }
295
-
296
- writeLine(stdout, `Updated navigation folder "${String(data.name ?? resolved.name ?? resolved.id)}".`);
297
- });
298
-
299
- folders
300
- .command("delete")
301
- .description("Delete a navigation folder")
302
- .argument("<folder>", "folder UUID or exact name", parseUuidOrSlugSelector)
303
- .action(async function action(this: Command, folder: NormalizedUuidOrSlug) {
304
- const { api, json, readOnly } = createNavigationApi(this, filePath);
305
- ensureWritable("navigation folders delete", readOnly, "destructive");
306
- const allFolders = await api.listFolders();
307
- const resolved = resolveFolder(allFolders, folder);
308
- const data = await api.deleteFolder({ kind: "uuid", value: resolved.id });
309
-
310
- if (json) {
311
- emitJson(stdout, "navigation folders delete", data, filePath);
312
- return;
313
- }
314
-
315
- writeLine(stdout, `Deleted navigation folder "${resolved.name ?? resolved.id}".`);
316
- });
317
-
318
- navigation
319
- .command("resources")
320
- .description("Manage navigation resources")
321
- .command("move")
322
- .description(
323
- "Move a schema into a navigation folder. Only schemas can be moved (the navigation menu holds databases); schedules and sub-schemas follow their parent schema. Use the root folder UUID 00000000-0000-0000-0000-000000000000 to move it back to the workspace root.",
324
- )
325
- .argument("<resource>", "schema UUID")
326
- .requiredOption("--to <folder>", "target folder UUID or exact name")
327
- .action(async function action(
328
- this: Command,
329
- resourceId: string,
330
- commandOptions: { to: string },
331
- ) {
332
- const { api, json, readOnly } = createNavigationApi(this, filePath);
333
- ensureWritable("navigation resources move", readOnly, "write");
334
- const allFolders = await api.listFolders();
335
- const resolved = resolveFolder(allFolders, parseUuidOrSlugSelector(commandOptions.to));
336
- const data = await api.moveResource({
337
- resourceId,
338
- targetFolderId: resolved.id,
339
- });
340
-
341
- if (json) {
342
- emitJson(stdout, "navigation resources move", data, filePath);
343
- return;
344
- }
345
-
346
- writeLine(stdout, `Moved resource "${resourceId}" to "${resolved.name ?? resolved.id}".`);
347
- });
348
- }