diff --git a/docs/reference.md b/docs/reference.md index 09308b0d3..c1b1abe73 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1651,12 +1651,15 @@ These commands help you manage scheduled and configured Actor runs. Use them to ```sh DESCRIPTION - Run saved Apify tasks (named Actor configurations). Only 'task run' is - available; create and manage tasks in Apify Console. + Run and publish saved Apify tasks (named Actor configurations). Create and + manage tasks in Apify Console. SUBCOMMANDS - task run Executes predefined Actor task remotely using local - key-value store for input. + task run Executes predefined Actor task remotely using + local key-value store for input. + task publish Publishes the task on its public landing page. + task unpublish Unpublishes the task from its public landing + page. ``` ##### `apify task run` @@ -1683,6 +1686,39 @@ FLAGS -t, --timeout= Timeout for the Task run in seconds. Zero value means there is no timeout. ``` + +##### `apify task publish` + +```sh +DESCRIPTION + Publishes the task on its public landing page. + The task must belong to a public Actor and have its public display + configuration set up on the task Publication tab in Apify Console. Requires + write access to the task and to its Actor. + +USAGE + $ apify task publish + +ARGUMENTS + taskId Name of the task to publish. For example, "my-task" or + "username/my-task". +``` + +##### `apify task unpublish` + +```sh +DESCRIPTION + Unpublishes the task from its public landing page. + The public display configuration is preserved, so the task can be published + again later. Requires write access to the task and to its Actor. + +USAGE + $ apify task unpublish + +ARGUMENTS + taskId Name of the task to unpublish. For example, "my-task" or + "username/my-task". +``` diff --git a/package.json b/package.json index 719230ccc..ef999ffed 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "@skyra/jaro-winkler": "^1.1.1", "adm-zip": "~0.6.0", "ajv": "~8.20.0", - "apify-client": "^2.23.3", + "apify-client": "^2.25.0", "archiver": "~8.0.0", "axios": "^1.15.0", "chalk": "~6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d37777901..928902905 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,8 +268,8 @@ importers: specifier: ~8.20.0 version: 8.20.0 apify-client: - specifier: ^2.23.3 - version: 2.24.0 + specifier: ^2.25.0 + version: 2.25.0 archiver: specifier: ~8.0.0 version: 8.0.0 @@ -4562,8 +4562,8 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - apify-client@2.24.0: - resolution: {integrity: sha512-z82DywSpMc6djz+HwEO9YFPAdxChOCKVWpzF0p8rH7hp5rXk5KuPqpmtwC9c3LiiRwTPRC/6y0JxbMqD2ndvwQ==} + apify-client@2.25.0: + resolution: {integrity: sha512-g6ttHJ4Au/Gg0iDHIevN3CvTbJfey9DUXUXJxe0ORF6JIYaVVfKdyJjINGb6/49gOpt/62ZFPw+HSEVIzrwR5w==} apify@3.7.2: resolution: {integrity: sha512-I4R5Qd1X11vk5/1U0CxKSsMhNwER/3MntePEQb9Pbtf942OhQLh5IURrJZufqtpimkqRy44SlQMU6UhPdoFXhA==} @@ -14527,7 +14527,7 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 - apify-client@2.24.0: + apify-client@2.25.0: dependencies: '@apify/consts': 2.56.1 '@apify/log': 2.5.47 @@ -14555,7 +14555,7 @@ snapshots: '@crawlee/core': 3.17.0 '@crawlee/types': 3.17.0 '@crawlee/utils': 3.17.0 - apify-client: 2.24.0 + apify-client: 2.25.0 fs-extra: 11.4.0 ow: 0.28.2 semver: 7.8.5 diff --git a/scripts/generate-cli-docs.ts b/scripts/generate-cli-docs.ts index 8f1335bd6..d1e22bb0b 100644 --- a/scripts/generate-cli-docs.ts +++ b/scripts/generate-cli-docs.ts @@ -116,6 +116,8 @@ const categories: Record = { // { command: Commands.task }, { command: Commands.taskRun }, + { command: Commands.taskPublish }, + { command: Commands.taskUnpublish }, ], 'mcp': [ // diff --git a/src/commands/task/_index.ts b/src/commands/task/_index.ts index f44286d6e..07edcaaf8 100644 --- a/src/commands/task/_index.ts +++ b/src/commands/task/_index.ts @@ -1,16 +1,18 @@ import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { TaskPublishCommand } from './publish.js'; import { TaskRunCommand } from './run.js'; +import { TaskUnpublishCommand } from './unpublish.js'; export class TasksIndexCommand extends ApifyCommand { static override name = 'task' as const; - static override description = `Run saved Apify tasks (named Actor configurations). Only 'task run' is available; create and manage tasks in Apify Console.`; + static override description = `Run and publish saved Apify tasks (named Actor configurations). Create and manage tasks in Apify Console.`; static override group = 'Apify Console'; static override docsUrl = 'https://docs.apify.com/cli/docs/reference#apify-task'; - static override subcommands = [TaskRunCommand]; + static override subcommands = [TaskRunCommand, TaskPublishCommand, TaskUnpublishCommand]; async run() { this.printHelp(); diff --git a/src/commands/task/publish.ts b/src/commands/task/publish.ts new file mode 100644 index 000000000..bd43ea488 --- /dev/null +++ b/src/commands/task/publish.ts @@ -0,0 +1,73 @@ +import process from 'node:process'; + +import type { ApifyApiError } from 'apify-client'; +import chalk from 'chalk'; + +import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { Args } from '../../lib/command-framework/args.js'; +import { CommandExitCodes } from '../../lib/consts.js'; +import { error, success } from '../../lib/outputs.js'; +import { getLocalUserInfo, getLoggedClientOrThrow } from '../../lib/utils.js'; + +export class TaskPublishCommand extends ApifyCommand { + static override name = 'publish' as const; + + static override description = + 'Publishes the task on its public landing page.\n' + + 'The task must belong to a public Actor and have its public display configuration set up ' + + 'on the task Publication tab in Apify Console. ' + + 'Requires write access to the task and to its Actor.'; + + static override examples = [ + { + description: 'Publish a task by name.', + command: 'apify task publish my-task', + }, + { + description: 'Publish a task by full name.', + command: 'apify task publish username/my-task', + }, + ]; + + static override docsUrl = 'https://docs.apify.com/cli/docs/reference#apify-task-publish'; + + static override args = { + taskId: Args.string({ + required: true, + description: 'Name of the task to publish. For example, "my-task" or "username/my-task".', + }), + }; + + async run() { + const apifyClient = await getLoggedClientOrThrow(); + const userInfo = await getLocalUserInfo(); + const usernameOrId = userInfo.username || (userInfo.id as string); + + const { taskId } = this.args; + const idOrName = taskId.includes('/') ? taskId : `${usernameOrId}/${taskId.toLowerCase()}`; + const taskClient = apifyClient.task(idOrName); + + const task = await taskClient.get(); + if (!task) { + error({ message: `Cannot find Task with name '${taskId}' in your account.` }); + process.exitCode = CommandExitCodes.NotFound; + return; + } + + try { + await taskClient.publish(); + + success({ + message: `Task ${chalk.yellow(task.name)} has been published.`, + stdout: true, + }); + } catch (err) { + const casted = err as ApifyApiError; + + error({ + message: `Failed to publish task ${chalk.yellow(task.name)}\n ${casted.message || casted}`, + }); + process.exitCode = CommandExitCodes.RunFailed; + } + } +} diff --git a/src/commands/task/unpublish.ts b/src/commands/task/unpublish.ts new file mode 100644 index 000000000..26c2a310e --- /dev/null +++ b/src/commands/task/unpublish.ts @@ -0,0 +1,72 @@ +import process from 'node:process'; + +import type { ApifyApiError } from 'apify-client'; +import chalk from 'chalk'; + +import { ApifyCommand } from '../../lib/command-framework/apify-command.js'; +import { Args } from '../../lib/command-framework/args.js'; +import { CommandExitCodes } from '../../lib/consts.js'; +import { error, success } from '../../lib/outputs.js'; +import { getLocalUserInfo, getLoggedClientOrThrow } from '../../lib/utils.js'; + +export class TaskUnpublishCommand extends ApifyCommand { + static override name = 'unpublish' as const; + + static override description = + 'Unpublishes the task from its public landing page.\n' + + 'The public display configuration is preserved, so the task can be published again later. ' + + 'Requires write access to the task and to its Actor.'; + + static override examples = [ + { + description: 'Unpublish a task by name.', + command: 'apify task unpublish my-task', + }, + { + description: 'Unpublish a task by full name.', + command: 'apify task unpublish username/my-task', + }, + ]; + + static override docsUrl = 'https://docs.apify.com/cli/docs/reference#apify-task-unpublish'; + + static override args = { + taskId: Args.string({ + required: true, + description: 'Name of the task to unpublish. For example, "my-task" or "username/my-task".', + }), + }; + + async run() { + const apifyClient = await getLoggedClientOrThrow(); + const userInfo = await getLocalUserInfo(); + const usernameOrId = userInfo.username || (userInfo.id as string); + + const { taskId } = this.args; + const idOrName = taskId.includes('/') ? taskId : `${usernameOrId}/${taskId.toLowerCase()}`; + const taskClient = apifyClient.task(idOrName); + + const task = await taskClient.get(); + if (!task) { + error({ message: `Cannot find Task with name '${taskId}' in your account.` }); + process.exitCode = CommandExitCodes.NotFound; + return; + } + + try { + await taskClient.unpublish(); + + success({ + message: `Task ${chalk.yellow(task.name)} has been unpublished.`, + stdout: true, + }); + } catch (err) { + const casted = err as ApifyApiError; + + error({ + message: `Failed to unpublish task ${chalk.yellow(task.name)}\n ${casted.message || casted}`, + }); + process.exitCode = CommandExitCodes.RunFailed; + } + } +}