---
name: harbor-exec
user-invocable: false
description: "Provides one-off TypeScript execution on Harbor Cloud when the work needs code: plugin discovery, compact input/output schema and output-path inspection, plugin calls, chaining, pagination, fan-out, transforms, compact result shaping, smoke tests, runtime probes, and publishing defineJob or deployApp source files with trace evidence."
---

# Harbor Exec

## Use When

- The user wants a one-off remote TypeScript run against the active Harbor workspace.
- The work needs code around connected tools: pagination, fan-out, filtering, grouping, joining, retries, or compact report formatting.
- The agent needs to smoke test runtime primitives or plugin availability.
- The agent needs to publish a defineJob or deployApp source file and keep the publish tied to a run id.
- The agent needs to turn several plugin outputs into a small final object instead of dumping raw plugin JSON.

## Do Not Use When

- The user only needs local control-plane discovery. Use hrbr inspect.
- The user wants a reusable backend primitive. Author defineJob and publish that file with hrbr exec -f.
- The user wants a routed UI. Author deployApp and publish that file with hrbr exec -f.
- The user wants product lifecycle APIs inside runtime code. Runtime code may invoke promoted jobs and workspace primitives; it must not declare app/job lifecycle functions.

## Surface Split

Inspect and exec intentionally expose different function surfaces.

    inspect                         exec runtime
    -------                         ------------
    hrbr.auth.*                     plugin namespace globals
    hrbr.workspace.*                hrbr.storage/cache/db/ai/tools/jobs
    hrbr.sources.*                  defineJob global for authoring files
    hrbr.tools.search({ ... })      deployApp global for authoring files
    hrbr.jobs/apps list/inspect     fetch

Rules:

- Use hrbr inspect 'return await hrbr.sources.list()' for source readiness and OAuth state.
- Use hrbr inspect 'return await hrbr.tools.search({ query: "...", source: "..." })' for exact plugin tool names, compact input/output schemas, output paths, and call examples.
- Use hrbr exec 'return await hrbr.tools.search("...")' only for in-runtime discovery. The runtime form accepts a string first; the inspect form accepts the richer object query.
- Use direct plugin namespace calls in one-off exec, for example return await <pluginRuntimeVar>.<toolFunction>({ <input> }).
- Use hrbr.jobs.someJob(input) in exec to invoke a promoted job.
- Use hrbr exec -f ./job.ts or hrbr exec -f ./app.ts to publish authoring source. Do not write hrbr.jobs.publish(...) or hrbr.apps.publish(...) inside runtime code.
- Treat run_id as the receipt for every exec. Capture it from the TOON envelope when the user needs proof, follow-up inspection, or deployment evidence.

## Inspect As A Research Step

Inspect accepts JavaScript. For non-trivial work, make the first inspect call a compact planner: gather workspace/source state once, run independent tool searches concurrently, project candidates to call + compact schema/path contracts, and return the next exec contract. Do not use `Object.keys(result)` as discovery; it proves almost nothing about callable behavior.

Default projection for tool hits:

    const compactHit = (hit) => ({
      tool_id: hit.tool_id,
      name: hit.name,
      call: hit.call,
      input_schema: hit.input_schema,
      output_schema: hit.output_schema,
      output_paths: hit.output_paths,
    });

Use `call`, `input_schema`, `output_schema`, and `output_paths` as the primary source of truth. Pull the raw nested contract only when the compact schemas or paths are insufficient. The inspect result should answer: which sources are ready, which tools are callable, what input shape the exec must use, what output paths to pick from, and what blockers remain.

Search before coding whenever a tool shape is not already known. Copy the returned `call`, compact schemas, and output paths from inspect output. Do not infer names from display labels or natural language.

## Canonical Sequence

    # 1. Verify local auth/workspace and source state when needed.
    hrbr inspect 'return await hrbr.auth.status()'
    hrbr inspect 'return await hrbr.sources.list()'

    # 2. Inspect exact plugin call shape, compact schemas, and output paths before invoking tools.
    hrbr inspect 'return await hrbr.tools.search({ query: "<capability phrase>", source: "<source-namespace>" })'

    # 3. Execute a compact one-off run that shapes output inside the runtime.
    hrbr exec 'return await <pluginRuntimeVar>.<toolFunction>({ <input> })'

    # 4. Publish reusable source through exec when needed.
    hrbr exec -f ./my-job.job.ts
    hrbr exec -f ./my-app.app.ts

## Input Modes

Prefer -f for anything that may need iteration.

    inline   hrbr exec 'return { ok: true }'      short, no complex quoting
    file     hrbr exec -f ./probe.ts              default for real work
    stdin    cat ./probe.ts | hrbr exec --stdin   generated source

Avoid shell-reshaping the output. Do grouping, sorting, truncation, and table logging inside the exec code. Return a compact object; console.log lines are captured in the run record.

## Runtime Patterns

Shape plugin output before returning. Prefer counts, IDs, URLs, titles, status fields, and explicit error snippets over full response bodies.

Use a small helper when a plugin result shape may vary:

    const arrayFrom = (value) => {
      if (Array.isArray(value)) return value;
      for (const key of ["items", "results", "data", "issues", "nodes"]) {
        if (Array.isArray(value?.[key])) return value[key];
      }
      return [];
    };

    const response = await pluginRuntimeVar.toolFunction({ limit: 5 });
    const rows = arrayFrom(response);
    return {
      count: rows.length,
      sample: rows.slice(0, 3).map((row) => ({
        title: row.title,
        url: row.url,
      })),
    };

For complex work, return an object with stable keys:

    return {
      ok: true,
      summary: "3 records matched the task criteria; no critical blocker was found.",
      evidence: {
        source_a: { matchingRecords: 3, failingChecks: [] },
        source_b: { criticalItems: 0 },
        source_c: { blockers: ["TASK-123"] },
      },
      nextActions: [
        "Ask the owner to confirm TASK-123 disposition",
      ],
    };

Use Promise.allSettled for independent calls so one failed plugin does not erase the whole diagnostic:

    const tasks = {
      search: () => pluginRuntimeVar.toolFunction({ limit: 3 }),
      jobs: () => hrbr.jobs.promotedJobName({ input: "value" }),
    };

    const entries = Object.entries(tasks);
    const settled = await Promise.allSettled(entries.map(([, run]) => run()));
    return Object.fromEntries(entries.map(([name], index) => {
      const result = settled[index];
      return result.status === "fulfilled"
        ? [name, { ok: true, value: result.value }]
        : [name, { ok: false, error: String(result.reason?.message ?? result.reason).slice(0, 400) }];
    }));

## Choosing Exec, Job, Or App

- Use exec for one-off research, diagnostics, smoke tests, and quick transforms.
- Use a job when the workflow should be reused, accepts typed input, powers an app, or touches plugins on behalf of an app.
- Use an app when a human needs a standalone UI, review surface, or repeated interaction.
- Use a job-backed app when the UI needs connected plugin data. Keep plugin calls in the job and let the app render or trigger the job result.
- Use markdown recipes before raw templates for untested combinations. Promote to a TS template only after the pattern has been verified with the local CLI.

## More Detail

Read only the reference you need:

- references/runtime-boundaries.md: exact inspect/exec/job/app boundary rules.
- references/tool-discovery-and-inspect.md: how to search tools, combine inspect calls, and extract the callable shape.
- references/result-shaping.md: how to normalize plugin results and return compact pipeline-friendly output.
- references/exec-patterns.md: pagination, fan-out, output shaping, and recovery examples.
- references/combination-recipes.md: markdown-first recipes for multi-plugin research, triage, release, incident, and app-backed workflows.
- references/exec-job-app-selection.md: when to stay in exec, promote to a job, or build a job-backed app.
- assets/templates/exec-plugin-call.ts: one-off plugin call template.
- assets/templates/exec-call-job.ts: invoke a promoted job from exec.
- assets/templates/exec-runtime-probe.ts: quick runtime surface probe.
- assets/templates/exec-normalize-results.ts: compact result normalization template.
- assets/templates/exec-multi-inspect.js: local inspect research snippet to paste into hrbr inspect.

## Done Criteria

- Source readiness, tool schema summaries, and output paths were checked with hrbr inspect before plugin calls when the tool shape was not already known.
- The exec returned a compact result object and did not require local sed, awk, temp files, or JSON scraping.
- Multi-line or iterative code was run from hrbr exec -f.
- App/job authoring used top-level deployApp or defineJob files published through exec.
- Runtime code did not invent hrbr.sources._, hrbr.workspace._, hrbr.jobs.publish(...), or hrbr.apps.publish(...).
- Combination examples that have not been tested locally stayed in markdown recipe form rather than being presented as runnable templates.
