metergraph-cli 0.0.0-stage → 0.1.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.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: metergraph
3
+ description: Route Metergraph setup by client, execution runtime and deployment, then perform bounded evidence-backed investigation when already authenticated.
4
+ ---
5
+
6
+ # Use Metergraph with your agent
7
+
8
+ Public setup: https://www.metergraph.dev/docs/guides/agent-access/
9
+ Connection and troubleshooting: https://www.metergraph.dev/docs/guides/agent-access/
10
+
11
+ ## Confirm setup before producing instructions
12
+
13
+ Ask for or confirm these non-secret choices. Do not guess from the agent brand:
14
+
15
+ 1. Client: Claude Desktop, Claude Code, ChatGPT or Codex.
16
+ 2. Execution runtime: customer machine or cloud. Codex local app/CLI/IDE is distinct from cloud execution. Claude Desktop remote connectors execute in Anthropic's cloud; Desktop local MCP is a separate mechanism. ChatGPT MCP apps execute in the cloud.
17
+ 3. Deployment: Metergraph hosted cloud, the signed commercial customer-local bundle, a customer-owned AWS installation (`byoc-core`), or the open-source self-hosted server. These have different addresses, accounts and credentials. Staging is not a customer setup path.
18
+ 4. Fresh workspace/installation or connecting an existing workspace. Ask for the intended workspace name and deployment origin, never a token.
19
+
20
+ Use the routing below with those four choices. Follow the matching credential and client configuration instructions in the connection guide.
21
+
22
+ ## Route honestly
23
+
24
+ | Client and runtime | Hosted | Customer-local bundle | Customer AWS | Open source self-hosted |
25
+ | --- | --- | --- | --- | --- |
26
+ | Claude Code or Codex on the customer machine | Keyed HTTP at `https://app.metergraph.dev/v1/agent/mcp` | Keyed HTTP at the installed local address, normally `http://127.0.0.1:8080/v1/agent/mcp` | Keyed HTTP at the customer's reachable installation address | Static `MG_AGENT_TOKENS` bearer at the OSS server address, normally `http://localhost:8787/v1/agent/mcp` |
27
+ | Claude Desktop remote connector on an individual account | Custom connector with a scoped bearer key if Request headers are available for the account; verify tools before claiming success | Cannot reach localhost from Anthropic's cloud | Default private installation is unreachable from Anthropic's cloud; no validated public route in this guide | Cannot reach localhost from Anthropic's cloud |
28
+ | Claude Desktop local MCP | Separate local extension required; not supported by this guide | Docker does not install a host extension | Separate local extension required; not supported by this guide | Docker does not install a host extension |
29
+ | ChatGPT MCP app or cloud execution | Separate cloud client configuration; not supported by this guide | Cannot reach localhost | Default private installation is unreachable | Cannot reach localhost |
30
+
31
+ For hosted Claude Desktop on an individual account, guide the human through Customize → Connectors → Add custom connector with `https://app.metergraph.dev/v1/agent/mcp`. They create a coding-agent key in the intended hosted workspace. If Request headers are available, choose No sign-in and enter a required header named `Authorization` with value `Bearer <key>` in the connector's private settings, never in chat. Request headers are in beta and may be unavailable for the account; do not claim the route works without them. They must enable the connector in a conversation. Team and Enterprise connectors may share fixed credentials across users, so do not put one person's workspace key in a shared connector. Do not present OAuth server code, a tool listing, local CLI installation or a saved configuration as connection success. Do not invent a released plugin, extension, tunnel or signup path. If a route is blocked, name the blocker and offer the customer-machine Claude Code/Codex keyed HTTP route or hosted deployment as appropriate. Do not expose localhost publicly or disable authentication as a workaround.
32
+
33
+ For a fresh hosted workspace, first ask whether the human already has a Metergraph account and workspace. Direct them to https://app.metergraph.dev/ and the signup, invitation or sign-in flow actually offered. The hosted UI can offer "Get a free API key" for self-service signup, but do not assume that button or a workspace invitation is available to everyone. If access is unavailable, direct them to their workspace owner or Metergraph. Confirm the workspace before creating a coding-agent key.
34
+
35
+ For the commercial customer-local bundle, direct them to https://www.metergraph.dev/docs/self-host/local/#the-customer-local-bundle and the signed public release. They need a separate pull-only registry credential from Metergraph to pull the images; the public bundle download does not grant registry access. Verify the release before installation. Local admin credentials from the bundle's private configuration provide dashboard sign-in; create a coding-agent key in that installation's Keys page. Do not substitute hosted signup or a hosted key. Do not clone the private implementation or run development seed commands.
36
+
37
+ For customer-owned AWS, direct them to https://www.metergraph.dev/docs/self-host/aws/. An operator provisions the installation, identity and membership. There is no public signup. Ask for the installation's actual reachable address and use a coding-agent key from its Keys page. Its default internal load balancer is private, and its Agent Access surface excludes trace content and replay.
38
+
39
+ For the open-source self-hosted server, direct them to https://www.metergraph.dev/docs/self-host/local/#the-open-source-server. No Metergraph account, commercial release, registry credential or Keys page is needed. The operator configures static `MG_AGENT_TOKENS` separately from ingestion `MG_TOKENS`. Agent Access is content-blind and does not provide replay, incidents, ingestion health or pipeline reports. Use the actual local server address; do not ask them to mint a hosted coding-agent key.
40
+
41
+ ## Keep credentials outside chat
42
+
43
+ Direct the human to the correct workspace's Keys page and private client configuration. Leave replay disabled unless separately authorized. Never ask for, quote, log or include raw keys in a prompt. Registry pull, ingestion, Agent Access and model/provider credentials are different credentials. Use the client's supported secure environment/settings input. Keep token-bearing configuration out of shell history and committed project files. Rotation/revocation happens in Keys; a revoked token must be rejected on the next request.
44
+
45
+ ## Answer capability and documentation questions
46
+
47
+ Use https://www.metergraph.dev/docs/ for public product documentation and https://www.metergraph.dev/docs/guides/mcp-server/ for Agent Access tool details. Explain what the docs say about an edition, then use `metergraph_get_capabilities` to confirm what the connected workspace actually supports. Cite the public page you used and distinguish a missing capability from an empty workspace. Do not read retained content or run replay just to answer a documentation question.
48
+
49
+ ## When already connected: bounded investigation
50
+
51
+ 1. Call `metergraph_get_workspace_context` and `metergraph_get_capabilities` first. Verify the intended workspace, deployment profile, available tools, required scopes and privacy classes. Stop on wrong workspace, unavailable capabilities or failed authorization.
52
+ 2. Confirm the question, route and explicit time window. Choose the smallest available tool: bounded usage/route metadata for aggregate questions, incidents for known anomalies, or `metergraph_query_traces` with a small limit (start with 1) for a failed/anomalous trace. Follow cursors only within the agreed bound. Never request generic SQL or unbounded data.
53
+ 3. Start with metadata. Retained-content debugging and report evidence are separate content-bearing tools; confirm the user's content intent and capability before calling them. Replay is a separate scope and can send data to a provider and spend money. Obtain explicit bounded execution/data-egress authority before any replay. Read access grants no eval-write, analysis-run or production-mutation authority.
54
+ 4. Explain the failure or anomaly using only returned evidence. Cite workspace, time window, provenance, trace/report IDs, completeness/coverage, warnings and working app links when returned. Do not fabricate links. Distinguish no data, incomplete evidence, unavailable capability, classification pending, stale/mismatched identity, failed analysis and missing report. An empty result alone does not prove absence of traffic.
55
+ 5. An optional approved replay must be eligible, bounded and non-persistent. Do not claim that it changed production telemetry or fixed the application. Refuse autonomous dashboard mutations or production configuration changes.
56
+
57
+ For an empty workspace, use https://www.metergraph.dev/docs/agents/instrument-a-repo/ after confirming the ingestion destination and obtaining a separate ingest credential securely. Do not initiate provider calls or paid work merely to validate setup. Ingestion accepted, processed, retained content, classification readiness and analysis readiness are distinct states. Label synthetic/demo fixtures; they do not prove a first real application trace.
@@ -0,0 +1,9 @@
1
+ {
2
+ "manifest_version": 1,
3
+ "name": "metergraph",
4
+ "file": "SKILL.md",
5
+ "source_url": "https://www.metergraph.dev/SKILL.md",
6
+ "sha256": "90f7d8d78a5b0b7a57436f194222f0c73310b0b04201c297c8fbf0b00ad6bb3f",
7
+ "size": 9149,
8
+ "revision": "sha256-90f7d8d78a5b"
9
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../src/cli.js";
3
+
4
+ // A closed pipe on stdout (for example "metergraph --help | head -1") is not
5
+ // a CLI failure.
6
+ process.stdout.on("error", () => {});
7
+
8
+ process.exitCode = await main(process.argv.slice(2), {
9
+ stdout: process.stdout,
10
+ stderr: process.stderr,
11
+ });
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "metergraph-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Preview Metergraph command line tool with a read-only connection probe and a project skill installer",
5
+ "type": "module",
6
+ "bin": {
7
+ "metergraph": "bin/metergraph.js"
8
+ },
9
+ "exports": {
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "bin/metergraph.js",
14
+ "src/*.js",
15
+ "assets/skill/SKILL.md",
16
+ "assets/skill/manifest.json",
17
+ "LICENSE",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "test": "node --test test/*.test.js",
22
+ "test:package": "node --test test/package/*.test.js"
23
+ },
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
27
+ "license": "Apache-2.0",
28
+ "author": "Metergraph",
29
+ "homepage": "https://www.metergraph.dev/",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/metergraph/cli.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/metergraph/cli/issues"
36
+ },
37
+ "keywords": [
38
+ "metergraph",
39
+ "cli",
40
+ "llm",
41
+ "cost-tracking"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ }
46
+ }
package/src/args.js ADDED
@@ -0,0 +1,180 @@
1
+ import {
2
+ DEFAULT_ORIGIN,
3
+ DEFAULT_TIMEOUT_MS,
4
+ HANDOFF_SKILL_CLIENTS,
5
+ HANDOFF_SKILL_RUNTIMES,
6
+ MAX_TIMEOUT_MS,
7
+ MIN_TIMEOUT_MS,
8
+ SKILL_CLIENTS,
9
+ SKILL_RUNTIMES,
10
+ } from "./constants.js";
11
+ import { parseOrigin } from "./origin.js";
12
+
13
+ const COMMANDS = new Set(["doctor", "help", "skill"]);
14
+ const HELP_TOPICS = new Set(["doctor", "skill"]);
15
+ const SKILL_ACTIONS = new Set(["install", "update"]);
16
+ const OPTIONS = {
17
+ doctor: new Set(["--url", "--timeout-ms"]),
18
+ skill: new Set(["--client", "--runtime", "--project"]),
19
+ };
20
+
21
+ // Parses argv into one of:
22
+ // { ok: true, command: "help", topic, json }
23
+ // { ok: true, command: "version", json }
24
+ // { ok: true, command: "doctor", origin, timeoutMs, json }
25
+ // { ok: true, command: "skill", action, client, runtime, project, json }
26
+ // { ok: false, command, json, code, message }
27
+ // Error messages are fixed strings. They never contain an argument value,
28
+ // because a mistyped argument can be a credential.
29
+ export function parseArgs(argv) {
30
+ const json = argv.includes("--json");
31
+
32
+ if (argv.includes("--help") || argv.includes("-h")) {
33
+ const topic = argv.find((arg) => HELP_TOPICS.has(arg)) ?? null;
34
+ return { ok: true, command: "help", topic, json };
35
+ }
36
+
37
+ let command = null;
38
+ let action = null;
39
+ let topic = null;
40
+ let version = false;
41
+ const values = {};
42
+
43
+ const fail = (code, message) => ({
44
+ ok: false,
45
+ command: command === "skill" && action !== null ? `skill ${action}` : command ?? (version ? "version" : null),
46
+ json,
47
+ code,
48
+ message,
49
+ });
50
+
51
+ for (let index = 0; index < argv.length; index += 1) {
52
+ const arg = argv[index];
53
+ const position = index + 1;
54
+
55
+ if (arg === "--json") continue;
56
+
57
+ if (command === null && !version && COMMANDS.has(arg)) {
58
+ command = arg;
59
+ continue;
60
+ }
61
+ if (command === null && arg === "--version") {
62
+ version = true;
63
+ continue;
64
+ }
65
+ if (command === "help" && topic === null && HELP_TOPICS.has(arg)) {
66
+ topic = arg;
67
+ continue;
68
+ }
69
+ if (command === "skill" && action === null && SKILL_ACTIONS.has(arg)) {
70
+ action = arg;
71
+ continue;
72
+ }
73
+
74
+ if (command === "doctor" || command === "skill") {
75
+ const equals = arg.indexOf("=");
76
+ const name = equals === -1 ? arg : arg.slice(0, equals);
77
+ if (OPTIONS[command].has(name)) {
78
+ let value;
79
+ if (equals === -1) {
80
+ index += 1;
81
+ value = argv[index];
82
+ } else {
83
+ value = arg.slice(equals + 1);
84
+ }
85
+ if (value === undefined) {
86
+ return fail("missing_value", `${name} requires a value.`);
87
+ }
88
+ if (values[name] !== undefined) {
89
+ return fail("duplicate_option", `${name} may be given only once.`);
90
+ }
91
+ values[name] = value;
92
+ continue;
93
+ }
94
+ }
95
+
96
+ if (command === null && !version && !arg.startsWith("-")) {
97
+ return fail(
98
+ "unknown_command",
99
+ `Unknown command at argument ${position}. Run "metergraph --help" for usage.`,
100
+ );
101
+ }
102
+ if (command === "skill" && action === null && !arg.startsWith("-")) {
103
+ return fail(
104
+ "unknown_subcommand",
105
+ `Unknown skill subcommand at argument ${position}. Use install or update.`,
106
+ );
107
+ }
108
+ return fail(
109
+ "unknown_argument",
110
+ `Unrecognized argument at position ${position}. Run "metergraph --help" for usage.`,
111
+ );
112
+ }
113
+
114
+ if (version) return { ok: true, command: "version", json };
115
+ if (command === null || command === "help") {
116
+ return { ok: true, command: "help", topic, json };
117
+ }
118
+ if (command === "skill") return parseSkill(action, values, json, fail);
119
+
120
+ const rawUrl = values["--url"];
121
+ const origin = rawUrl === undefined ? DEFAULT_ORIGIN : parseOrigin(rawUrl);
122
+ if (origin === null) {
123
+ return fail(
124
+ "invalid_url",
125
+ "--url must be a bare https origin such as https://metergraph.example.com, " +
126
+ "or an http origin on localhost, 127.0.0.1 or [::1]. " +
127
+ "Credentials, paths, queries and fragments are not accepted.",
128
+ );
129
+ }
130
+
131
+ const rawTimeout = values["--timeout-ms"];
132
+ const timeoutMs =
133
+ rawTimeout === undefined ? DEFAULT_TIMEOUT_MS : parseTimeout(rawTimeout);
134
+ if (timeoutMs === null) {
135
+ return fail(
136
+ "invalid_timeout",
137
+ `--timeout-ms must be a whole number from ${MIN_TIMEOUT_MS} to ${MAX_TIMEOUT_MS}.`,
138
+ );
139
+ }
140
+
141
+ return { ok: true, command: "doctor", origin, timeoutMs, json };
142
+ }
143
+
144
+ // Client and runtime are required, so a script states where the skill is
145
+ // used instead of the CLI guessing. Values for clients and runtimes that
146
+ // cannot use project skill files are accepted here and handed off later.
147
+ function parseSkill(action, values, json, fail) {
148
+ if (action === null) {
149
+ return fail(
150
+ "missing_subcommand",
151
+ 'skill requires a subcommand: install or update. Run "metergraph help skill" for usage.',
152
+ );
153
+ }
154
+ const client = values["--client"];
155
+ if (client === undefined) {
156
+ return fail("missing_client", "--client is required. Use codex, claude or cursor.");
157
+ }
158
+ if (!Object.hasOwn(SKILL_CLIENTS, client) && !Object.hasOwn(HANDOFF_SKILL_CLIENTS, client)) {
159
+ return fail("invalid_client", "--client must be codex, claude or cursor.");
160
+ }
161
+ const runtime = values["--runtime"];
162
+ if (runtime === undefined) {
163
+ return fail("missing_runtime", "--runtime is required. Use local or cloud.");
164
+ }
165
+ if (!SKILL_RUNTIMES.includes(runtime) && !HANDOFF_SKILL_RUNTIMES.includes(runtime)) {
166
+ return fail("invalid_runtime", "--runtime must be local or cloud.");
167
+ }
168
+ const project = values["--project"];
169
+ if (project !== undefined && (project === "" || project.includes("\0"))) {
170
+ return fail("invalid_project", "--project must name an existing directory.");
171
+ }
172
+ return { ok: true, command: "skill", action, client, runtime, project: project ?? null, json };
173
+ }
174
+
175
+ function parseTimeout(raw) {
176
+ if (!/^[0-9]{1,6}$/.test(raw)) return null;
177
+ const value = Number(raw);
178
+ if (value < MIN_TIMEOUT_MS || value > MAX_TIMEOUT_MS) return null;
179
+ return value;
180
+ }
package/src/cli.js ADDED
@@ -0,0 +1,90 @@
1
+ import { parseArgs } from "./args.js";
2
+ import { runDoctor } from "./doctor.js";
3
+ import {
4
+ doctorMessage,
5
+ doctorText,
6
+ envelope,
7
+ helpData,
8
+ helpText,
9
+ skillText,
10
+ toJsonLine,
11
+ versionData,
12
+ } from "./output.js";
13
+ import { runSkill } from "./skill.js";
14
+ import { VERSION } from "./constants.js";
15
+
16
+ // Runs one command and resolves with the exit code. With --json, stdout gets
17
+ // exactly one JSON line and stderr stays empty. Without it, results go to
18
+ // stdout and usage or internal errors go to stderr. Never reads stdin.
19
+ export async function main(argv, { stdout, stderr }) {
20
+ const json = argv.includes("--json");
21
+ let result;
22
+ try {
23
+ result = await run(argv, { stdout, stderr });
24
+ } catch {
25
+ result = envelope({
26
+ command: null,
27
+ outcome: "internal_error",
28
+ reason: "unexpected_failure",
29
+ message: "The CLI failed unexpectedly. No details are shown to avoid exposing sensitive input.",
30
+ });
31
+ if (json) stdout.write(toJsonLine(result));
32
+ else stderr.write(`Error: ${result.error.message}\n`);
33
+ }
34
+ return result.exit_code;
35
+ }
36
+
37
+ async function run(argv, { stdout, stderr }) {
38
+ const parsed = parseArgs(argv);
39
+
40
+ if (!parsed.ok) {
41
+ const result = envelope({
42
+ command: parsed.command,
43
+ outcome: "invalid_input",
44
+ reason: parsed.code,
45
+ message: parsed.message,
46
+ });
47
+ if (parsed.json) stdout.write(toJsonLine(result));
48
+ else stderr.write(`Error: ${parsed.message}\n`);
49
+ return result;
50
+ }
51
+
52
+ if (parsed.command === "help") {
53
+ const result = envelope({ command: "help", outcome: "ok", data: helpData(parsed.topic) });
54
+ stdout.write(parsed.json ? toJsonLine(result) : helpText(parsed.topic));
55
+ return result;
56
+ }
57
+
58
+ if (parsed.command === "version") {
59
+ const result = envelope({ command: "version", outcome: "ok", data: versionData() });
60
+ stdout.write(parsed.json ? toJsonLine(result) : `${VERSION}\n`);
61
+ return result;
62
+ }
63
+
64
+ if (parsed.command === "skill") {
65
+ const { outcome, reason, message, data } = runSkill(parsed);
66
+ const result = envelope({
67
+ command: `skill ${parsed.action}`,
68
+ outcome,
69
+ reason,
70
+ data,
71
+ message: outcome === "ok" ? null : message,
72
+ });
73
+ stdout.write(parsed.json ? toJsonLine(result) : skillText(result, message));
74
+ return result;
75
+ }
76
+
77
+ const { outcome, reason, report } = await runDoctor({
78
+ origin: parsed.origin,
79
+ timeoutMs: parsed.timeoutMs,
80
+ });
81
+ const result = envelope({
82
+ command: "doctor",
83
+ outcome,
84
+ reason,
85
+ data: report,
86
+ message: doctorMessage(outcome, reason),
87
+ });
88
+ stdout.write(parsed.json ? toJsonLine(result) : doctorText(result));
89
+ return result;
90
+ }
@@ -0,0 +1,81 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ const packageJson = JSON.parse(
4
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
5
+ );
6
+
7
+ export const PACKAGE_NAME = packageJson.name;
8
+ export const VERSION = packageJson.version;
9
+
10
+ export const SCHEMA_VERSION = 1;
11
+ export const DEFAULT_ORIGIN = "https://app.metergraph.dev";
12
+
13
+ export const DEFAULT_TIMEOUT_MS = 5000;
14
+ export const MIN_TIMEOUT_MS = 100;
15
+ export const MAX_TIMEOUT_MS = 30000;
16
+ export const MAX_BODY_BYTES = 32 * 1024;
17
+
18
+ // The public guide a person follows to connect an application to Metergraph.
19
+ // The CLI cannot sign in yet, so this is the only next action it offers.
20
+ export const CONNECTION_GUIDE_URL =
21
+ "https://www.metergraph.dev/docs/guides/agent-access/";
22
+
23
+ // Deployment profiles recognized by the probe contract. Any other value,
24
+ // including a well-formed one, is reported as unsupported and never echoed.
25
+ export const SUPPORTED_PROFILES = Object.freeze([
26
+ "local",
27
+ "managed",
28
+ "byoc-core",
29
+ ]);
30
+
31
+ // Clients that load project skills from a native directory, from each
32
+ // client's own documentation. Every client has its own directory, so
33
+ // installing for one never touches another client's files.
34
+ export const SKILL_CLIENTS = Object.freeze({
35
+ codex: Object.freeze({ label: "Codex", dir: ".agents" }),
36
+ claude: Object.freeze({ label: "Claude Code", dir: ".claude" }),
37
+ cursor: Object.freeze({ label: "Cursor", dir: ".cursor" }),
38
+ });
39
+ export const SKILL_RUNTIMES = Object.freeze(["local", "cloud"]);
40
+
41
+ // Recognized values that cannot load project skill files. They get a pointer
42
+ // to the connection guide instead of a usage error, and nothing is written.
43
+ export const HANDOFF_SKILL_CLIENTS = Object.freeze({
44
+ "claude-desktop": "Claude Desktop",
45
+ chatgpt: "ChatGPT",
46
+ });
47
+ export const HANDOFF_SKILL_RUNTIMES = Object.freeze(["cloud-no-shell"]);
48
+
49
+ // One exit code per outcome. Documented in README.md; changing a value is a
50
+ // breaking change for scripts. New outcomes are appended.
51
+ export const EXIT_CODES = Object.freeze({
52
+ ok: 0,
53
+ internal_error: 1,
54
+ invalid_input: 2,
55
+ authentication_required: 3,
56
+ connection_failed: 4,
57
+ unhealthy: 5,
58
+ unsupported: 6,
59
+ redirect_rejected: 7,
60
+ conflict: 8,
61
+ filesystem_error: 9,
62
+ });
63
+
64
+ export const EXIT_CODE_MEANINGS = Object.freeze({
65
+ ok: "Command succeeded. Doctor does not return this in this preview.",
66
+ internal_error: "Unexpected internal failure in the CLI.",
67
+ invalid_input: "Unknown command or argument, or an invalid option value. No request was made.",
68
+ authentication_required:
69
+ "Service is reachable, healthy and supported, and requires authentication. No workspace is connected.",
70
+ connection_failed: "The origin could not be reached, the connection failed, or the probe timed out.",
71
+ unhealthy:
72
+ "The service answered but reported that it is not healthy, or answered with a server error.",
73
+ unsupported:
74
+ "The service answered with a response, deployment profile or status this CLI does not support, " +
75
+ "or the skill client or runtime cannot use project skill files. Nothing was written.",
76
+ redirect_rejected: "The service answered with a redirect. Redirects are never followed.",
77
+ conflict:
78
+ "The skill target is not owned by this CLI, was modified, is unsafe, is locked or needs an explicit update. Nothing was changed.",
79
+ filesystem_error:
80
+ "Project files could not be read or written. Partial changes were rolled back unless the message says otherwise.",
81
+ });