Skip to content

Release a bundle with the artific command

The concept behind publications is explained in Sharing and reuse.

artific is a command that releases a bundle kept as files. The prompts, agents, toolsets and other members of a bundle sit in a directory you keep in git, and the command checks that directory against Core, tries it out in a test organisation, publishes it as a new version and submits it for the marketplace. It calls the same routes as Publish and install through the API, so what it publishes is an ordinary publication: subscribers install and sync it exactly as one you publish in the console.

Use it when a bundle changes through pull requests, or when a pipeline should publish it. For a bundle you assemble and publish by hand, the console is the shorter route.

publish, try and submit are safe to repeat: they write only what differs from what Core holds, and a second run against an unchanged project writes nothing and says so.

What you need

  • A key of the organisation that publishes, holding management. The command acts for that organisation and for nothing else. Manage organisations through the API shows how to switch that capability on; Keys and policy in the console shows how to create a key and keep its secret.
  • The address of your Core, such as https://core.example.com.

That is all. Your own Core key, as ARTIFIC_API_KEY, with your Core's address, as ARTIFIC_ENDPOINT, is the only credential the command and its GitHub Action ever need. You need no GitHub account, token or access to an Artific repository: the downloads and the action are public. The token Artific's own release workflow uses is Artific's alone; it only publishes new versions of the command and is never one you create.

Install the command

Each release of the command is a GitHub release of the public repository artific-ai/artific-cli with the tag v<version>, such as v0.1.0. It holds one archive per system and a SHA256SUMS file with the checksum of each archive. An archive holds the artific binary alone. You need no account or token to download one.

Your system Archive
Linux, x86-64 artific-<version>-x86_64-unknown-linux-musl.tar.gz
Linux, ARM64 artific-<version>-aarch64-unknown-linux-musl.tar.gz
macOS, Apple silicon artific-<version>-aarch64-apple-darwin.tar.gz
macOS, Intel artific-<version>-x86_64-apple-darwin.tar.gz

The Linux binaries are static, so they run on any distribution and in any container. There is no Windows build.

To install one:

  1. Download the archive for your system and the checksums. On an Apple silicon Mac:

    VERSION=0.1.0
    ARCHIVE=artific-$VERSION-aarch64-apple-darwin.tar.gz
    BASE=https://github.com/artific-ai/artific-cli/releases/download/v$VERSION
    curl -fLO "$BASE/$ARCHIVE"
    curl -fLO "$BASE/SHA256SUMS"
    
  2. Check the archive against SHA256SUMS. The command prints <archive>: OK; anything else means the download is not the released file, so delete it and do not unpack it.

    grep " $ARCHIVE\$" SHA256SUMS | shasum -a 256 -c
    

    On Linux, sha256sum -c does the same as shasum -a 256 -c.

  3. Unpack the binary and move it to a directory on your PATH that you can write to. For /usr/local/bin you often need sudo:

    tar -xzf "$ARCHIVE" artific
    sudo mv artific /usr/local/bin/
    artific --version
    

Always name an exact version: the archive name carries it, and there is no "latest". A released version never changes; a fix comes in a later version.

Point it at Core

Every step except artific build reads two environment variables:

export ARTIFIC_ENDPOINT=https://core.example.com
export ARTIFIC_API_KEY=ack_...

ARTIFIC_ENDPOINT is the address of your Core without /api/v1: the command adds the path of each route itself. This differs from the CORE_URL the other pages of this guide use.

The address must start with https://, so the key never travels unencrypted; http:// is accepted only for localhost, 127.0.0.1 and [::1]. The key is sent only in the Authorization header, and nothing the command prints contains it. When a variable is missing, the command stops with artific: ARTIFIC_ENDPOINT is not set (or ARTIFIC_API_KEY).

Lay out the project

A bundle is a directory. Long text sits in files of its own, so a pull request shows a changed prompt as a text diff:

support-desk/
  artific.yaml                  the bundle: name, description, visibility, terms, parameters
  members/
    prompt/triage.yaml          one member per file: the kind is the directory, the name the file
    prompt/triage.template.md   text that triage.yaml includes with !file
    agent/desk.yaml
    toolset/desk-tools.yaml
    script/house_deck.yaml      a script member
    script/house_deck/          that script's files
  packages/
    deckkit-1.0.0-py3-none-any.whl   a package file a script carries
  covers/
    listing.png                 the picture on the catalogue card
    agent/desk.png              the cover of one member
  CHANGELOG.md                  its top section becomes the version's changelog

The quickest way to a correct project is to build the bundle once in the console, publish a version, and pull it: the command writes that version as a directory in exactly this shape, and you continue from there.

The bundle file

artific.yaml describes the publication and the parts of the bundle that belong to no member:

name: support-desk
description: Triage and answer support tickets.
visibility: subtree
terms:
  fork: false
parameters:
  company:
    type: string
    description: The company name the agent uses in its answers.
Key What it is
name The publication's name in your organisation: lowercase letters, digits, - and _, starting with a letter or a digit, at most 64 characters. Required.
description What the bundle is for, at most 1,024 characters.
visibility subtree (organisations below yours), tenants (the organisations in targets) or catalogue (the marketplace). Without this key the bundle goes to the marketplace.
targets For tenants only: the ids of the organisations that may subscribe.
auto_install For subtree only: true subscribes every new organisation below yours straight away.
terms The version's terms: fork, sealed, redistribute, own_agents_only, entitlement, license and price, as described in Publish and install in the console.
parameters, requires, policy Written as in a manifest; a pulled project shows them for a version you already published.

visibility, targets and auto_install take effect when the command creates the publication. After that, a publication's name and visibility stay as they are.

Members

Each file under members/<kind>/ is one member: its kind is the directory (prompt, agent, toolset, script, …), its name is the file name without .yaml, and its content is the member's definition. A member refers to another member of the bundle as {$member: <name>}, and to a parameter as {$param: <name>}. A member installed only for a certain choice of the installer states that condition under $when.

# members/prompt/triage.yaml
description: Sorts an incoming ticket.
template: !file triage.template.md

!file reads a text file, relative to the YAML file that names it. It cannot name a file outside the project directory, and no other YAML tag is accepted, so a project never reads anything from the machine it is built on.

A script's files are the directory with the script's name next to its member file (members/script/house_deck/). The command lists every file in it with its size and checksum, so the member file itself never states files. That directory may hold only regular files, not symbolic links. A package that only your organisation approved travels with the bundle (Packages in a bundle): the member names it with ecosystem, name, version and filename, and the file sits in packages/ under that filename. The command adds its size and checksum.

Secrets never go in a project. A connector's credential is something the installer supplies, so no field of a member can hold one.

Covers and changelog

covers/listing.png is the picture on the catalogue card. A member cover goes in covers/<kind>/<name>.png, for an agent, a toolset, an API connector, an MCP server or a tool alias. A cover is a .png, .jpg or .webp of at most 8 MiB.

The changelog of a version is the text under the first ## heading of CHANGELOG.md, up to the next one, at most 4,096 characters. The heading itself, such as ## 1.4.0, names the release for you; Core numbers its versions itself, 1, 2, 3 and on.

One manifest file instead

A bundle may also stay one manifest.json: the manifest field of what GET /publications/{pid}/versions/{v} returns. Next to it, artific.yaml then holds only what the manifest does not: visibility, targets, auto_install and terms. The command refuses an artific.yaml that states the name, the description or anything else the manifest already holds.

See what the project builds

artific build --project support-desk

prints the one manifest the project builds and contacts nothing. Every step except pull takes --project, which names the project directory or a manifest.json; it defaults to the current directory.

Release a version

Step What it does
artific check Asks Core whether it would accept the bundle. Publishes nothing.
artific try --org <id> Installs the bundle in a test organisation as a preview.
artific publish Creates the publication when needed and publishes a version when the bundle changed.
artific submit Submits a marketplace bundle for review.

Check

artific check --project support-desk

Core validates the bundle with the same rules a publish runs: every member, every reference and parameter, and the terms. On success the command says whether a publish would write a version and which capabilities a subscriber's organisation needs:

support-desk: accepted; publish would write version 4
capabilities a subscriber needs: tooling, computer

On a refusal it prints each field Core named, with Core's own message, and exits with code 1. A price without an entitlement, for example (Set a price):

artific: checking support-desk: 422 Unprocessable Content: ...
  terms.price: ...

A check belongs to a publication, so the very first release of a bundle cannot be checked yet; the command says artific publish creates the publication. Core checks a script only over files the publication holds, so check uploads any script or package file the publication does not hold yet. It writes no version.

Try it in a test organisation

An organisation cannot install its own marketplace bundle. To see the install as a subscriber will see it, try the bundle in an organisation below yours:

artific try --project support-desk --org ten_9Fb2...

This publishes the bundle as a separate publication, support-desk-preview, visible only to that organisation, and subscribes the organisation to it. Open the test organisation and look under Installed: the parameters are asked, the capabilities checked and the copies written as for any subscriber. When the command prints subscribed, needs_configuration, a required parameter has no value yet: fill it in on the subscription under Installed (Installing). Run try again after a change and the preview gets the new version.

The preview's terms never ask for an entitlement and carry no price, because a test organisation holds no entitlement. A preview stays until you withdraw it:

artific try --project support-desk --withdraw

A withdrawal is final and the name stays taken, so the next preview is called support-desk-preview-2, and so on. While a preview targets one organisation, a try in another is refused until you withdraw it.

Publish

artific publish --project support-desk

The command creates the publication when your organisation has none by that name: a marketplace publication as a draft, the others listed right away. It then uploads the script files and packages the publication does not hold yet, publishes a version when the manifest or the terms differ from the highest version, uploads covers whose bytes changed and sets the description when it changed. It prints one line for each thing it wrote:

publication support-desk: created (listed)
version 1: published
cover listing: uploaded

What happens to a new version depends on the visibility:

  • Organisations below or named organisations: every subscription that follows the latest version syncs to it straight away. Run check and try first.
  • Marketplace: a draft is offered to nobody until Artific lists it. On a bundle already listed, the new version waits for review, and subscribers keep the version they have. A changed description of a listed marketplace bundle also waits, and the command says so.

Submit for the marketplace

artific submit --project support-desk

submits a marketplace draft for review, as Submit for review does in the console (Marketplace). A listed bundle with a newer version waits for review already, and the command leaves it alone. A bundle for organisations below or named organisations is never submitted; the command refuses it. Listing the bundle stays Artific's decision.

publish and submit are separate steps, so a pipeline can publish on every merge and submit only when you tag a release.

Turn a published version into files

artific pull support-desk
artific pull support-desk 3 --into support-desk-v3

writes a version of one of your organisation's publications as a project directory: the highest version unless you name one, into a directory with the publication's name unless you give --into. The directory must be empty or not exist yet. Multi-line text in a definition is written to <member>.<field>.md next to the member file, and the script files, packages, covers and the changelog come along. Building the pulled directory gives back the manifest, terms, changelog and covers of that version, so a bundle first assembled in the console becomes a project this way.

Messages and exit codes

Exit code Meaning
0 The step finished, whether or not it wrote anything.
1 Core refused the request, or the project or the environment is wrong. The message on standard error says what.
2 The command line does not parse: an unknown step or flag, or a missing argument.

An error line starts with artific: and names what the command was doing, such as artific: publishing a version of support-desk: ..., followed by the status and title of Core's answer when Core refused. A refusal that names fields follows with one indented line per field. artific <step> --help lists a step's flags.

Set a price

A bundle can show a price in the marketplace. It is a term of the version:

terms:
  entitlement: true
  price:
    amount: "49.00"
    currency: EUR
    period: month

period is once, month or year, and amount is a decimal with at most two decimals. Core stores the price and shows it, and never charges it: an organisation installs a bundle that asks for an entitlement only once it holds one, and the payment happens outside Core (When a bundle needs an entitlement). That is why a price without entitlement: true is refused (422 on terms.price). A changed price is changed terms, so every subscription waits for its organisation to accept the new version's terms; no subscriber moves to a new price unasked.

Release from a GitHub pipeline

The same public repository also holds a GitHub Action, artific-ai/artific-cli, that installs one release of the command, checks it against SHA256SUMS, and runs one step with it. It works in any repository, in any organisation, without a token: the only secret you need is the key.

Input What it is
version The exact release, such as 0.1.0. Required; there is no "latest".
command The step: build, check, try, publish, submit or pull.
args Further arguments on one line, such as --project bundles/support-desk. They are split on spaces, so an argument cannot contain one, and never expanded by a shell.
endpoint ARTIFIC_ENDPOINT. Every step except build needs it.
api-key ARTIFIC_API_KEY, from a secret.

A workflow that checks every pull request, publishes every merge to main, and submits on a tag:

name: bundles
on:
  pull_request:
  push:
    branches: [main]
    tags: ["v*"]

permissions:
  contents: read

jobs:
  check:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-24.04
    environment: core
    steps:
      - uses: actions/checkout@v4
      - uses: artific-ai/artific-cli@v0.1.0
        with:
          version: 0.1.0
          command: check
          args: --project bundles/support-desk
          endpoint: ${{ vars.ARTIFIC_ENDPOINT }}
          api-key: ${{ secrets.ARTIFIC_API_KEY }}

  release:
    if: github.event_name == 'push'
    runs-on: ubuntu-24.04
    environment: core
    steps:
      - uses: actions/checkout@v4
      - uses: artific-ai/artific-cli@v0.1.0
        with:
          version: 0.1.0
          command: ${{ github.ref_type == 'tag' && 'submit' || 'publish' }}
          args: --project bundles/support-desk
          endpoint: ${{ vars.ARTIFIC_ENDPOINT }}
          api-key: ${{ secrets.ARTIFIC_API_KEY }}

The GitHub environment core holds the address as the variable ARTIFIC_ENDPOINT and the key as the secret ARTIFIC_API_KEY. With several Core installations, such as a test and a production one, give each its own environment and run the step once per environment. A repository with several bundles repeats the step for each project.

  • Pin both. @v0.1.0 after uses: picks the action, version the binary; change them together. The tags of artific-ai/artific-cli are never moved. If your repository pins every action to a commit, name the full commit v0.1.0 points at, with the tag as a comment.
  • Check needs the publication. The first pull request of a new bundle fails at check until publish has created the publication once.
  • The key is a credential like any other. Rotate it as described in Rotate a key, and update the secret.