codiedev 0.9.80 → 0.10.0-beta.10
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.
- package/README.md +12 -5
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +0 -11
- package/dist/cli.js +11 -33
- package/dist/cliSession.js +3 -60
- package/dist/codexHookTrust.js +0 -28
- package/dist/codexLimitScan.js +0 -107
- package/dist/codie.js +64 -57
- package/dist/codieBrandInstall.js +0 -18
- package/dist/codieLauncher.js +1 -72
- package/dist/codieRuntime.js +27 -32
- package/dist/codieRuntimeInstall.js +11 -43
- package/dist/codieUsage.js +0 -49
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1088 -0
- package/dist/commands/agentsSdk.generated.js +7 -0
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +2 -72
- package/dist/commands/evals.js +3 -9
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +0 -29
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +0 -9
- package/dist/commands/push.js +0 -12
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +0 -13
- package/dist/commands/tracesLabel.js +0 -8
- package/dist/configDir.js +1 -18
- package/dist/connect.js +3 -75
- package/dist/connectFlow.js +0 -15
- package/dist/copilotHooks.js +0 -67
- package/dist/copilotInstallation.js +0 -1
- package/dist/cursorHooks.js +1 -11
- package/dist/cursorLifecycle.js +1 -7
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +0 -19
- package/dist/hook.js +43 -293
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +0 -50
- package/dist/launcher.js +0 -33
- package/dist/limitEvent.js +2 -34
- package/dist/managedPi.js +2 -50
- package/dist/mcp.js +0 -48
- package/dist/nativeSteering.js +1 -4
- package/dist/packs/codie.js +0 -15
- package/dist/packs/harnesses.js +1 -38
- package/dist/packs/hookGate.js +0 -33
- package/dist/packs/install.js +0 -20
- package/dist/packs/manifest.js +1 -15
- package/dist/packs/paths.js +0 -8
- package/dist/packs/shim.js +0 -8
- package/dist/packs/store.js +0 -12
- package/dist/packs/sync.js +0 -20
- package/dist/piHooks.js +4 -45
- package/dist/piRuntime.js +0 -18
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/selfUpdate.js +2 -77
- package/dist/skillMarker.js +0 -14
- package/dist/subagents.js +0 -18
- package/dist/teamSkillContext.js +0 -1
- package/dist/transcriptModels.js +0 -8
- package/dist/upgradeNudge.js +1 -30
- package/dist/utils.js +165 -768
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +13 -8
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/captureSignals.d.ts +0 -47
- package/dist/cli.d.ts +0 -15
- package/dist/cliSession.d.ts +0 -108
- package/dist/codexHookTrust.d.ts +0 -49
- package/dist/codexLimitScan.d.ts +0 -64
- package/dist/codie.d.ts +0 -2
- package/dist/codieBrandInstall.d.ts +0 -11
- package/dist/codieLauncher.d.ts +0 -87
- package/dist/codieRuntime.d.ts +0 -35
- package/dist/codieRuntimeInstall.d.ts +0 -56
- package/dist/codieUsage.d.ts +0 -64
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -9
- package/dist/commands/evals.d.ts +0 -3
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/pack.d.ts +0 -54
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/commands/traces.d.ts +0 -72
- package/dist/commands/tracesLabel.d.ts +0 -24
- package/dist/configDir.d.ts +0 -10
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -34
- package/dist/copilotHooks.d.ts +0 -85
- package/dist/copilotInstallation.d.ts +0 -10
- package/dist/cursorHooks.d.ts +0 -13
- package/dist/cursorLifecycle.d.ts +0 -2
- package/dist/detection.d.ts +0 -30
- package/dist/harnessDirs.d.ts +0 -15
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/launchContext.d.ts +0 -46
- package/dist/launcher.d.ts +0 -25
- package/dist/limitEvent.d.ts +0 -15
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/managedPi.d.ts +0 -48
- package/dist/mcp.d.ts +0 -12
- package/dist/nativeSteering.d.ts +0 -5
- package/dist/packs/codie.d.ts +0 -45
- package/dist/packs/harnesses.d.ts +0 -74
- package/dist/packs/hookGate.d.ts +0 -53
- package/dist/packs/install.d.ts +0 -46
- package/dist/packs/manifest.d.ts +0 -52
- package/dist/packs/paths.d.ts +0 -17
- package/dist/packs/shim.d.ts +0 -26
- package/dist/packs/store.d.ts +0 -42
- package/dist/packs/sync.d.ts +0 -40
- package/dist/piHooks.d.ts +0 -22
- package/dist/piRuntime.d.ts +0 -26
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/selfUpdate.d.ts +0 -64
- package/dist/skillMarker.d.ts +0 -6
- package/dist/subagents.d.ts +0 -36
- package/dist/teamSkillContext.d.ts +0 -9
- package/dist/transcriptModels.d.ts +0 -20
- package/dist/upgradeNudge.d.ts +0 -24
- package/dist/utils.d.ts +0 -396
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsPackage = exports.jsTypes = exports.jsIndex = exports.python = void 0;
|
|
4
|
+
exports.python = "\"\"\"Codiedev agents SDK — the run primitive, from Python.\n\nInside a deployed agent (everything is already configured):\n\n import codiedev_agents as agents\n\n task = agents.input() # this run's input (JSON)\n llm = agents.openai() # OpenAI client on the Codiedev gateway, traced\n child = agents.run(\"summarize\", {\"text\": ...}) # start another agent as a child run\n agents.output({\"summary\": child[\"output\"]}) # this run's result\n agents.request() # webhook runs: method, headers, raw body\n agents.respond({\"text\": \"on it\"}) # what a webhook caller waiting (?wait=) gets back\n\n agents.state.get(\"cursor\") # per-agent storage that outlives runs\n agents.state.set(\"cursor\", 42)\n agents.step(\"fetch\", lambda: fetch()) # done once per run, even across retries\n agents.start(\"triage\", {...}, delay=3600) # start a run later (up to 7 days)\n agents.request_approval(\"Refund $120?\", context={...}) # a person decides; a run acts on it\n agents.attempt() # 1, or 2+ when this run is a retry\n\nOutside a run (a script, a notebook, another service), set CODIEDEV_TOKEN\n(your CLI token) and CODIEDEV_AGENTS_URL, then call agents.run() the same way.\nStandard library only, so it drops into any project.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport os\nimport time\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom typing import Any\n\n__all__ = [\n \"input\", \"output\", \"request\", \"respond\", \"run\", \"start\", \"get\", \"openai\", \"attempt\",\n \"state\", \"step\", \"request_approval\", \"get_approval\", \"AgentRunError\", \"StateConflict\",\n]\n\n_INPUT_PATH = os.environ.get(\"CODIEDEV_INPUT_PATH\", \"/agent/.codiedev/input.json\")\n_OUTPUT_PATH = os.environ.get(\"CODIEDEV_OUTPUT_PATH\", \"/agent/.codiedev/output.json\")\n_REQUEST_PATH = os.environ.get(\"CODIEDEV_REQUEST_PATH\", \"/agent/.codiedev/request.json\")\n_RESPONSE_PATH = os.environ.get(\"CODIEDEV_RESPONSE_PATH\", \"/agent/.codiedev/response.json\")\n# Set by `codiedev agents run --local`: state lives in this file instead of Codiedev.\n_STATE_PATH = os.environ.get(\"CODIEDEV_STATE_PATH\")\n_DONE = {\"succeeded\", \"failed\", \"cancelled\"}\n\n\nclass StateConflict(RuntimeError):\n \"\"\"state.set(..., if_version=n) found the key at another version.\"\"\"\n\n def __init__(self, key: str, version: int):\n super().__init__(f\"state {key!r} changed (now version {version})\")\n self.key = key\n self.version = version\n\n\nclass AgentRunError(RuntimeError):\n def __init__(self, run: dict):\n super().__init__(f\"{run.get('agent')} run {run.get('id')} {run.get('status')}: {run.get('error') or 'no error message'}\")\n self.run = run\n\n\ndef input(default: Any = None) -> Any: # noqa: A001 - mirrors output()\n \"\"\"This run's input, parsed from JSON (None when started without one).\"\"\"\n try:\n with open(_INPUT_PATH) as f:\n value = json.load(f)\n except FileNotFoundError:\n return default\n return default if value is None else value\n\n\ndef output(value: Any) -> None:\n \"\"\"Set this run's result. Must be JSON-serializable; the last call wins.\"\"\"\n os.makedirs(os.path.dirname(_OUTPUT_PATH), exist_ok=True)\n with open(_OUTPUT_PATH, \"w\") as f:\n json.dump(value, f)\n\n\ndef respond(body: Any = None, status: int = 200, headers: \"dict | None\" = None) -> None:\n \"\"\"What a webhook caller gets back when it waits for the run (?wait=N):\n a string is sent as text, anything else as JSON. Without respond(), a\n waiting caller gets the run's output.\"\"\"\n os.makedirs(os.path.dirname(_RESPONSE_PATH), exist_ok=True)\n with open(_RESPONSE_PATH, \"w\") as f:\n json.dump({\"status\": status, \"headers\": headers or {}, \"body\": body}, f)\n\n\ndef attempt() -> int:\n \"\"\"This run's attempt: 1, or 2+ when it's a retry of a failed attempt.\"\"\"\n return int(os.environ.get(\"CODIEDEV_ATTEMPT\", \"1\"))\n\n\ndef request() -> \"dict | None\":\n \"\"\"For a webhook run: {\"method\", \"query\", \"headers\", \"body\"} with the raw\n body, to verify the sender's signature. None for other runs.\"\"\"\n try:\n with open(_REQUEST_PATH) as f:\n return json.load(f)\n except FileNotFoundError:\n return None\n\n\ndef _base() -> str:\n url = os.environ.get(\"CODIEDEV_AGENTS_URL\", \"\").rstrip(\"/\")\n if not url:\n raise RuntimeError(\"Set CODIEDEV_AGENTS_URL (it's set for you inside a run)\")\n return url\n\n\ndef _token() -> str:\n # Inside a run: the run's token. A connected agent (running outside Codiedev): its own key.\n # Otherwise: your CLI token.\n key = os.environ.get(\"CODIEDEV_API_KEY\", \"\")\n token = os.environ.get(\"CODIEDEV_RUN_TOKEN\") or (key if key.startswith(\"cdvm_\") else None) or os.environ.get(\"CODIEDEV_TOKEN\")\n if not token:\n raise RuntimeError(\"Set CODIEDEV_API_KEY to your connected agent's key, or CODIEDEV_TOKEN to your CLI token (inside a run it's set for you)\")\n return token\n\n\ndef _request(method: str, path: str, body: dict | None = None, ok: tuple[int, ...] = ()) -> dict:\n req = urllib.request.Request(\n f\"{_base()}{path}\",\n method=method,\n data=json.dumps(body).encode() if body is not None else None,\n headers={\"Authorization\": f\"Bearer {_token()}\", \"Content-Type\": \"application/json\"},\n )\n try:\n with urllib.request.urlopen(req, timeout=30) as res:\n return json.loads(res.read() or b\"{}\")\n except urllib.error.HTTPError as err:\n detail = err.read().decode(errors=\"replace\")\n if err.code in ok:\n return {\"_status\": err.code, **json.loads(detail or \"{}\")}\n try:\n detail = json.loads(detail).get(\"error\", detail)\n except ValueError:\n pass\n raise RuntimeError(f\"Codiedev agents API {err.code}: {detail}\") from None\n\n\ndef start(\n agent: str,\n input: Any = None, # noqa: A002\n idempotency_key: str | None = None,\n delay: float | None = None,\n retries: int | None = None,\n) -> str:\n \"\"\"Start a run and return its id without waiting. With an idempotency key,\n a second start within a day returns the first run's id. delay (seconds,\n up to 7 days) starts it later; retries overrides the agent's retries.\"\"\"\n body: dict = {\"agent\": agent, \"input\": input}\n if idempotency_key:\n body[\"idempotencyKey\"] = idempotency_key\n if delay:\n body[\"delaySec\"] = int(delay)\n if retries is not None:\n body[\"retries\"] = retries\n return _request(\"POST\", \"/api/agents/v1/run\", body)[\"runId\"]\n\n\ndef get(run_id: str) -> dict:\n \"\"\"A run's status, input, output (parsed), error, log tail and children.\"\"\"\n run = _request(\"GET\", f\"/api/agents/v1/run?id={run_id}\")\n if isinstance(run.get(\"output\"), str):\n try:\n run[\"output\"] = json.loads(run[\"output\"])\n except ValueError:\n pass\n return run\n\n\ndef run(\n agent: str,\n input: Any = None, # noqa: A002\n timeout: float = 900,\n poll: float = 2.0,\n check: bool = True,\n idempotency_key: str | None = None,\n) -> dict:\n \"\"\"Run an agent and wait for it. Returns the run; its result is run[\"output\"].\n\n Inside a run this starts a child run, so the chain shows as one tree.\n Raises AgentRunError if it fails (pass check=False to get the run anyway).\n \"\"\"\n run_id = start(agent, input, idempotency_key)\n deadline = time.monotonic() + timeout\n while True:\n result = get(run_id)\n if result.get(\"status\") in _DONE:\n if check and result[\"status\"] != \"succeeded\":\n raise AgentRunError(result)\n return result\n if time.monotonic() > deadline:\n raise TimeoutError(f\"{agent} run {run_id} still {result.get('status')} after {timeout}s\")\n time.sleep(poll)\n\n\nclass _State:\n \"\"\"Per-agent key-value storage that outlives runs (values: any JSON, up\n to 64 KB; 1,000 keys). Every write bumps the key's version; pass\n if_version to write only if nobody else did since you read it.\"\"\"\n\n def get(self, key: str, default: Any = None) -> Any:\n value, _ = self.get_versioned(key)\n return default if value is None else value\n\n def get_versioned(self, key: str) -> \"tuple[Any, int]\":\n \"\"\"(value, version); (None, 0) when the key isn't set.\"\"\"\n if _STATE_PATH:\n row = _local_state().get(key)\n return (row[\"value\"], row[\"version\"]) if row else (None, 0)\n res = _request(\"GET\", f\"/api/agents/v1/state?{urllib.parse.urlencode({'key': key, 'agent': _agent()})}\", ok=(404,))\n if res.get(\"_status\") == 404:\n return None, 0\n return json.loads(res[\"value\"]), int(res[\"version\"])\n\n def set(self, key: str, value: Any, if_version: \"int | None\" = None, ttl: \"float | None\" = None) -> int:\n \"\"\"Store value; returns the new version. Raises StateConflict when\n if_version is given and the key is at another version (0 = unset).\"\"\"\n return self._write(key, value, if_version, ttl)\n\n def delete(self, key: str, if_version: \"int | None\" = None) -> None:\n self._write(key, None, if_version, None)\n\n def list(self, prefix: str = \"\", values: bool = False) -> \"list[dict]\": # noqa: A003\n \"\"\"Keys (and with values=True, values) starting with prefix.\"\"\"\n if _STATE_PATH:\n rows = sorted((k, r) for k, r in _local_state().items() if k.startswith(prefix))\n return [{\"key\": k, \"version\": r[\"version\"], **({\"value\": r[\"value\"]} if values else {})} for k, r in rows]\n keys: list = []\n after = None\n while True:\n params = {\"prefix\": prefix, \"values\": \"1\" if values else \"0\", \"limit\": 200, \"agent\": _agent()}\n if after:\n params[\"after\"] = after\n page = _request(\"GET\", f\"/api/agents/v1/state?{urllib.parse.urlencode(params)}\")\n for row in page[\"keys\"]:\n if \"value\" in row:\n row[\"value\"] = json.loads(row[\"value\"])\n keys.extend(page[\"keys\"])\n after = page.get(\"next\")\n if not after:\n return keys\n\n def update(self, key: str, fn, default: Any = None, retries: int = 10) -> Any:\n \"\"\"Read-modify-write that's safe with runs in parallel: fn(old) -> new,\n retried if another run wrote first. Returns the new value.\"\"\"\n for _ in range(retries):\n value, version = self.get_versioned(key)\n new = fn(default if value is None else value)\n try:\n self.set(key, new, if_version=version)\n return new\n except StateConflict:\n time.sleep(0.05)\n raise StateConflict(key, -1)\n\n def _write(self, key: str, value: Any, if_version: \"int | None\", ttl: \"float | None\") -> int:\n if _STATE_PATH:\n data = _local_state()\n current = data.get(key, {}).get(\"version\", 0)\n if if_version is not None and if_version != current:\n raise StateConflict(key, current)\n if value is None:\n data.pop(key, None)\n else:\n data[key] = {\"value\": value, \"version\": current + 1}\n with open(_STATE_PATH, \"w\") as f:\n json.dump(data, f, indent=2)\n return 0 if value is None else current + 1\n body: dict = {\"key\": key, \"value\": value, \"agent\": _agent()}\n if if_version is not None:\n body[\"ifVersion\"] = if_version\n if ttl:\n body[\"ttlSec\"] = int(ttl)\n res = _request(\"POST\", \"/api/agents/v1/state\", body, ok=(409,))\n if res.get(\"_status\") == 409:\n raise StateConflict(key, int(res.get(\"version\", 0)))\n return int(res.get(\"version\", 0))\n\n\ndef _agent() -> str:\n # Inside a run the run token picks the agent; a CLI token needs it named.\n return os.environ.get(\"CODIEDEV_AGENT\", \"\")\n\n\ndef _local_state() -> dict:\n try:\n with open(_STATE_PATH or \"\") as f:\n return json.load(f)\n except (FileNotFoundError, ValueError):\n return {}\n\n\nstate = _State()\n\n\ndef step(name: str, fn, ttl: float = 7 * 86400) -> Any:\n \"\"\"Run fn() once for this run and remember its (JSON) result: if the run\n is retried after a failure, finished steps return their saved result\n instead of running again. Use it around side effects (posting a reply,\n charging a card) so a retry doesn't repeat them.\"\"\"\n key = f\"step/{os.environ.get('CODIEDEV_RUN_ID', 'local')}/{name}\"\n value, version = state.get_versioned(key)\n if version:\n return value[\"result\"]\n result = fn()\n state.set(key, {\"result\": result}, ttl=ttl)\n return result\n\n\n_SELF = object()\n\n\ndef request_approval(\n title: str,\n details: Any = None,\n options: \"list[str] | None\" = None,\n approvers: \"list[str] | None\" = None,\n teams: \"list[str] | None\" = None,\n fields: \"list[dict] | None\" = None,\n then: Any = _SELF,\n context: Any = None,\n expires_in: \"float | None\" = None,\n) -> dict:\n \"\"\"Ask a person to decide. It shows in Codiedev (Agents → Approvals) and\n at the returned [\"url\"], which you can post anywhere (Slack, email).\n\n When it's decided, a run of `then` (this agent by default; None for no\n run) starts with input {\"approval\": {\"decision\", \"comment\", \"decidedBy\",\n ...}, \"context\": context}. If nobody decides within expires_in seconds\n (default 7 days), it starts with decision \"expired\". Usually the run\n ends after asking, so nothing runs (or costs) while it waits.\n options default to [\"approve\", \"reject\"]; approvers (emails) and teams\n (team names, e.g. [\"Finance\"]) limit who can decide (admins always can).\n Apps your company connects (Agents → Approvals → Apps) are told about it\n by webhook and can decide on a person's behalf.\n\n fields asks the person to fill something in as they decide, e.g.\n [{\"name\": \"rating\", \"type\": \"rating\"}, {\"name\": \"why\", \"type\": \"textarea\"}]\n (types: text, textarea, number, rating, select, multiselect, boolean;\n required, label, help, options, min, max, default). What they entered\n arrives as input[\"approval\"][\"values\"]. With fields and no options, the\n only choice is \"submit\".\"\"\"\n body: dict = {\"title\": title, \"details\": details, \"options\": options, \"approvers\": approvers, \"teams\": teams, \"fields\": fields, \"context\": context}\n if then is not _SELF:\n body[\"onDecision\"] = then\n if expires_in:\n body[\"expiresSec\"] = int(expires_in)\n if os.environ.get(\"CODIEDEV_LOCAL\") == \"1\":\n print(f\"[local] approval requested: {title} ({', '.join(options or (['submit'] if fields else ['approve', 'reject']))}); not sent on a local run\", flush=True)\n return {\"id\": f\"local-{int(time.time())}\", \"status\": \"pending\", \"url\": None, \"title\": title}\n return _request(\"POST\", \"/api/agents/v1/approvals\", {k: v for k, v in body.items() if v is not None or k == \"onDecision\"})\n\n\ndef get_approval(approval_id: str) -> dict:\n \"\"\"An approval's status (\"pending\", \"decided\", \"expired\"), decision and comment.\"\"\"\n return _request(\"GET\", f\"/api/agents/v1/approvals?id={urllib.parse.quote(approval_id)}\")\n\n\ndef wait_for_approval(approval_id: str, timeout: float = 3600, poll: float = 15) -> dict:\n \"\"\"Wait until a person decides (or it expires), then return the approval. For agents that keep\n running while they wait, like a connected agent; a run inside Codiedev usually ends instead and\n is started again with the decision.\"\"\"\n deadline = time.time() + timeout\n while True:\n a = get_approval(approval_id)\n if a.get(\"status\") != \"pending\":\n return a\n if time.time() > deadline:\n raise TimeoutError(f\"approval {approval_id} still pending after {timeout}s\")\n time.sleep(poll)\n\n\ndef openai(**kwargs):\n \"\"\"An OpenAI client on the Codiedev gateway, with trace headers for this run.\"\"\"\n from openai import OpenAI\n\n headers = {\n \"X-Codiedev-Workload\": os.environ.get(\"CODIEDEV_AGENT\", \"local-agent\"),\n \"X-Codiedev-Run-Id\": os.environ.get(\"CODIEDEV_RUN_ID\", f\"local-{int(time.time())}\"),\n \"X-Codiedev-Environment\": \"production\" if os.environ.get(\"CODIEDEV_RUN_ID\") else \"development\",\n }\n headers.update(kwargs.pop(\"default_headers\", {}) or {})\n return OpenAI(\n base_url=os.environ.get(\"CODIEDEV_GATEWAY_URL\", \"https://gateway.codiedev.com/api/v1\"),\n api_key=os.environ.get(\"CODIEDEV_API_KEY\") or os.environ.get(\"CODIEDEV_TOKEN\"),\n default_headers=headers,\n **kwargs,\n )\n";
|
|
5
|
+
exports.jsIndex = "// Codiedev agents SDK for JavaScript and TypeScript, preinstalled in every\n// run. Same primitive as the Python SDK (codiedev_agents.py):\n//\n// import * as agents from \"codiedev-agents\";\n// const task = agents.input(); // this run's input (JSON)\n// const llm = await agents.openai(); // OpenAI client on the Codiedev gateway, traced\n// const child = await agents.run(\"summarize\", { text }); // another agent, as a child run\n// agents.output({ summary: child.output }); // this run's result\n// agents.respond({ text: \"on it\" }); // what a webhook caller waiting (?wait=) gets back\n// await agents.state.get(\"cursor\"); // per-agent storage that outlives runs\n// await agents.step(\"post\", () => post()); // done once per run, even across retries\n// await agents.start(\"triage\", input, { delaySec: 3600 }); // start a run later\n// await agents.requestApproval(\"Refund $120?\", { context }); // a person decides; a run acts on it\n//\n// Outside a run, set CODIEDEV_TOKEN and CODIEDEV_AGENTS_URL and call run() the same way.\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst INPUT_PATH = process.env.CODIEDEV_INPUT_PATH || \"/agent/.codiedev/input.json\";\nconst OUTPUT_PATH = process.env.CODIEDEV_OUTPUT_PATH || \"/agent/.codiedev/output.json\";\nconst REQUEST_PATH = process.env.CODIEDEV_REQUEST_PATH || \"/agent/.codiedev/request.json\";\nconst RESPONSE_PATH = process.env.CODIEDEV_RESPONSE_PATH || \"/agent/.codiedev/response.json\";\n// Set by `codiedev agents run --local`: state lives in this file instead of Codiedev.\nconst STATE_PATH = process.env.CODIEDEV_STATE_PATH;\n// Inside a run the run token picks the agent; a CLI token needs it named.\nconst AGENT = process.env.CODIEDEV_AGENT || \"\";\nconst DONE = new Set([\"succeeded\", \"failed\", \"cancelled\"]);\n\n/** state.set(..., { ifVersion }) found the key at another version. */\nexport class StateConflict extends Error {\n constructor(key, version) {\n super(`state ${JSON.stringify(key)} changed (now version ${version})`);\n this.key = key;\n this.version = version;\n }\n}\n\nexport class AgentRunError extends Error {\n constructor(run) {\n super(`${run.agent} run ${run.id} ${run.status}: ${run.error || \"no error message\"}`);\n this.run = run;\n }\n}\n\nfunction readJson(file) {\n try {\n return JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch (err) {\n if (err.code === \"ENOENT\") return null;\n throw err;\n }\n}\n\n/** This run's input, parsed from JSON (`fallback` when started without one). */\nexport function input(fallback = null) {\n const value = readJson(INPUT_PATH);\n return value ?? fallback;\n}\n\n/** Set this run's result. Must be JSON-serializable; the last call wins. */\nexport function output(value) {\n fs.mkdirSync(path.dirname(OUTPUT_PATH), { recursive: true });\n fs.writeFileSync(OUTPUT_PATH, JSON.stringify(value));\n}\n\n/** What a webhook caller gets back when it waits for the run (?wait=N): a\n * string is sent as text, anything else as JSON. Without it, a waiting\n * caller gets the run's output. */\nexport function respond(body = null, { status = 200, headers = {} } = {}) {\n fs.mkdirSync(path.dirname(RESPONSE_PATH), { recursive: true });\n fs.writeFileSync(RESPONSE_PATH, JSON.stringify({ status, headers, body }));\n}\n\n/** This run's attempt: 1, or 2+ when it's a retry of a failed attempt. */\nexport function attempt() {\n return Number(process.env.CODIEDEV_ATTEMPT || \"1\");\n}\n\n/** For a webhook run: { method, query, headers, body } with the raw body, to verify a signature. */\nexport function request() {\n return readJson(REQUEST_PATH);\n}\n\nfunction base() {\n const url = (process.env.CODIEDEV_AGENTS_URL || \"\").replace(/\\/$/, \"\");\n if (!url) throw new Error(\"Set CODIEDEV_AGENTS_URL (it's set for you inside a run)\");\n return url;\n}\n\nfunction token() {\n // Inside a run: the run's token. A connected agent (running outside Codiedev): its own key.\n // Otherwise: your CLI token.\n const key = process.env.CODIEDEV_API_KEY || \"\";\n const t = process.env.CODIEDEV_RUN_TOKEN || (key.startsWith(\"cdvm_\") ? key : \"\") || process.env.CODIEDEV_TOKEN;\n if (!t) throw new Error(\"Set CODIEDEV_API_KEY to your connected agent's key, or CODIEDEV_TOKEN to your CLI token (inside a run it's set for you)\");\n return t;\n}\n\nasync function call(method, route, body, ok = []) {\n const res = await fetch(`${base()}${route}`, {\n method,\n headers: { Authorization: `Bearer ${token()}`, \"Content-Type\": \"application/json\" },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const text = await res.text();\n let data = {};\n try {\n data = text ? JSON.parse(text) : {};\n } catch {\n data = { error: text };\n }\n if (ok.includes(res.status)) return { _status: res.status, ...data };\n if (!res.ok) throw new Error(`Codiedev agents API ${res.status}: ${data.error || text}`);\n return data;\n}\n\n/** Start a run and return its id without waiting. delaySec (up to 7 days)\n * starts it later; retries overrides the agent's retries. */\nexport async function start(agent, input = null, { idempotencyKey, delaySec, retries } = {}) {\n return (await call(\"POST\", \"/api/agents/v1/run\", {\n agent,\n input,\n ...(idempotencyKey ? { idempotencyKey } : {}),\n ...(delaySec ? { delaySec } : {}),\n ...(retries !== undefined ? { retries } : {}),\n })).runId;\n}\n\nfunction localState() {\n try {\n return JSON.parse(fs.readFileSync(STATE_PATH, \"utf8\"));\n } catch {\n return {};\n }\n}\n\nasync function writeState(key, value, { ifVersion, ttlSec } = {}) {\n if (STATE_PATH) {\n const data = localState();\n const current = data[key]?.version ?? 0;\n if (ifVersion !== undefined && ifVersion !== current) throw new StateConflict(key, current);\n if (value === null) delete data[key];\n else data[key] = { value, version: current + 1 };\n fs.writeFileSync(STATE_PATH, JSON.stringify(data, null, 2));\n return value === null ? 0 : current + 1;\n }\n const res = await call(\"POST\", \"/api/agents/v1/state\", { key, value, agent: AGENT, ...(ifVersion !== undefined ? { ifVersion } : {}), ...(ttlSec ? { ttlSec } : {}) }, [409]);\n if (res._status === 409) throw new StateConflict(key, res.version ?? 0);\n return res.version ?? 0;\n}\n\n/** Per-agent key-value storage that outlives runs (values: any JSON, up to\n * 64 KB; 1,000 keys). Every write bumps the key's version; pass ifVersion\n * to write only if nobody else did since you read it. */\nexport const state = {\n async get(key, fallback = null) {\n const { value } = await state.getVersioned(key);\n return value ?? fallback;\n },\n /** { value, version }; { value: null, version: 0 } when the key isn't set. */\n async getVersioned(key) {\n if (STATE_PATH) {\n const row = localState()[key];\n return row ? { value: row.value, version: row.version } : { value: null, version: 0 };\n }\n const res = await call(\"GET\", `/api/agents/v1/state?${new URLSearchParams({ key, agent: AGENT })}`, undefined, [404]);\n if (res._status === 404) return { value: null, version: 0 };\n return { value: JSON.parse(res.value), version: res.version };\n },\n /** Store value; returns the new version. Throws StateConflict on an ifVersion mismatch (0 = unset). */\n set(key, value, { ifVersion, ttlSec } = {}) {\n return writeState(key, value, { ifVersion, ttlSec });\n },\n async delete(key, { ifVersion } = {}) {\n await writeState(key, null, { ifVersion });\n },\n /** Keys (and with values: true, values) starting with prefix. */\n async list(prefix = \"\", { values = false } = {}) {\n if (STATE_PATH) {\n return Object.entries(localState())\n .filter(([k]) => k.startsWith(prefix))\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, row]) => ({ key, version: row.version, ...(values ? { value: row.value } : {}) }));\n }\n const out = [];\n let after = null;\n do {\n const q = new URLSearchParams({ prefix, values: values ? \"1\" : \"0\", limit: \"200\", agent: AGENT, ...(after ? { after } : {}) });\n const page = await call(\"GET\", `/api/agents/v1/state?${q}`);\n out.push(...page.keys.map((row) => (\"value\" in row ? { ...row, value: JSON.parse(row.value) } : row)));\n after = page.next;\n } while (after);\n return out;\n },\n /** Read-modify-write that's safe with runs in parallel: fn(old) => new, retried if another run wrote first. */\n async update(key, fn, fallback = null, retries = 10) {\n for (let i = 0; i < retries; i++) {\n const { value, version } = await state.getVersioned(key);\n const next = await fn(value ?? fallback);\n try {\n await state.set(key, next, { ifVersion: version });\n return next;\n } catch (err) {\n if (!(err instanceof StateConflict)) throw err;\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n }\n throw new StateConflict(key, -1);\n },\n};\n\n/** Run fn() once for this run and remember its (JSON) result: if the run is\n * retried after a failure, finished steps return their saved result instead\n * of running again. Use it around side effects so a retry doesn't repeat them. */\nexport async function step(name, fn, { ttlSec = 7 * 86400 } = {}) {\n const key = `step/${process.env.CODIEDEV_RUN_ID || \"local\"}/${name}`;\n const { value, version } = await state.getVersioned(key);\n if (version) return value.result;\n const result = await fn();\n await state.set(key, { result: result ?? null }, { ttlSec });\n return result;\n}\n\n/** Ask a person to decide. It shows in Codiedev (Agents → Approvals) and at\n * the returned .url, which you can post anywhere. When it's decided, a run of\n * `then` (this agent by default; null for none) starts with input\n * { approval: { decision, comment, decidedBy, ... }, context }. Undecided\n * after expiresSec (default 7 days): decision \"expired\". */\nexport async function requestApproval(title, { details, options, approvers, teams, fields, then, context, expiresSec } = {}) {\n if (process.env.CODIEDEV_LOCAL === \"1\") {\n console.log(`[local] approval requested: ${title} (${(options || (fields ? [\"submit\"] : [\"approve\", \"reject\"])).join(\", \")}); not sent on a local run`);\n return { id: `local-${Date.now()}`, status: \"pending\", url: null, title };\n }\n return call(\"POST\", \"/api/agents/v1/approvals\", {\n title,\n ...(details !== undefined ? { details } : {}),\n ...(options ? { options } : {}),\n ...(approvers ? { approvers } : {}),\n ...(teams ? { teams } : {}),\n ...(fields ? { fields } : {}),\n ...(then !== undefined ? { onDecision: then } : {}),\n ...(context !== undefined ? { context } : {}),\n ...(expiresSec ? { expiresSec } : {}),\n });\n}\n\n/** An approval's status (\"pending\", \"decided\", \"expired\"), decision and comment. */\nexport async function getApproval(id) {\n return call(\"GET\", `/api/agents/v1/approvals?id=${encodeURIComponent(id)}`);\n}\n\n/** Wait until a person decides (or it expires), then return the approval. For agents that keep\n * running while they wait, like a connected agent; a run inside Codiedev usually ends instead and\n * is started again with the decision. */\nexport async function waitForApproval(id, { timeoutMs = 3_600_000, pollMs = 15_000 } = {}) {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const a = await getApproval(id);\n if (a.status !== \"pending\") return a;\n if (Date.now() > deadline) throw new Error(`approval ${id} still pending after ${timeoutMs / 1000}s`);\n await new Promise((resolve) => setTimeout(resolve, pollMs));\n }\n}\n\n/** A run's status, input, output (parsed), error, log and children. */\nexport async function get(runId) {\n const run = await call(\"GET\", `/api/agents/v1/run?id=${encodeURIComponent(runId)}`);\n if (typeof run.output === \"string\") {\n try {\n run.output = JSON.parse(run.output);\n } catch {\n // not JSON: keep the string\n }\n }\n return run;\n}\n\n/** Run an agent and wait for it; its result is `.output`. Inside a run this\n * starts a child run. Throws AgentRunError if it fails unless check is false. */\nexport async function run(agent, input = null, { timeoutMs = 900_000, pollMs = 2000, check = true, idempotencyKey } = {}) {\n const id = await start(agent, input, { idempotencyKey });\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const r = await get(id);\n if (DONE.has(r.status)) {\n if (check && r.status !== \"succeeded\") throw new AgentRunError(r);\n return r;\n }\n if (Date.now() > deadline) throw new Error(`${agent} run ${id} still ${r.status} after ${timeoutMs / 1000}s`);\n await new Promise((resolve) => setTimeout(resolve, pollMs));\n }\n}\n\n/** Options for `new OpenAI(...)` on the Codiedev gateway, with this run's trace headers. */\nexport function openaiOptions(extra = {}) {\n const inRun = !!process.env.CODIEDEV_RUN_ID;\n return {\n baseURL: process.env.CODIEDEV_GATEWAY_URL || \"https://gateway.codiedev.com/api/v1\",\n apiKey: process.env.CODIEDEV_API_KEY || process.env.CODIEDEV_TOKEN,\n ...extra,\n defaultHeaders: {\n \"X-Codiedev-Workload\": process.env.CODIEDEV_AGENT || \"local-agent\",\n \"X-Codiedev-Run-Id\": process.env.CODIEDEV_RUN_ID || `local-${Date.now()}`,\n \"X-Codiedev-Environment\": inRun ? \"production\" : \"development\",\n ...(extra.defaultHeaders || {}),\n },\n };\n}\n\n/** An OpenAI client on the Codiedev gateway (the `openai` package is preinstalled). */\nexport async function openai(extra = {}) {\n const { default: OpenAI } = await import(\"openai\");\n return new OpenAI(openaiOptions(extra));\n}\n";
|
|
6
|
+
exports.jsTypes = "/* eslint-disable @typescript-eslint/no-explicit-any -- defaults are `any` on purpose: agent code reads its own JSON input/state without declaring types first. */\nexport type RunStatus = \"queued\" | \"running\" | \"succeeded\" | \"failed\" | \"cancelled\";\nexport interface Run<O = unknown> {\n id: string;\n agent: string;\n version: number;\n status: RunStatus;\n trigger: \"cli\" | \"api\" | \"portal\" | \"schedule\" | \"webhook\" | \"child\" | \"approval\";\n parentRunId: string | null;\n rootRunId: string | null;\n depth: number;\n attempt: number;\n maxAttempts: number;\n scheduledFor: number | null;\n input: string | null;\n output: O;\n error: string | null;\n logTail: string | null;\n durationMs: number | null;\n children: Run[];\n}\nexport interface WebhookRequest { method: string; query: Record<string, string>; headers: Record<string, string>; body: string }\nexport interface Approval {\n id: string;\n agent: string;\n title: string;\n details: string | null;\n options: string[];\n status: \"pending\" | \"decided\" | \"expired\" | \"cancelled\";\n decision: string | null;\n comment: string | null;\n decidedBy: string | null;\n /** The approval app that sent the decision on decidedBy's behalf; null when decided in Codiedev. */\n decidedVia: string | null;\n approvers: string[];\n approverTeams: Array<{ id: string; name: string }>;\n /** The form the person fills in, if the approval asked for one. */\n fields: ApprovalField[];\n /** What they entered, by field name (null until decided). */\n values: Record<string, any> | null;\n url: string | null;\n resumeRunId: string | null;\n}\n/** One thing a person fills in when they decide. */\nexport interface ApprovalField {\n name: string;\n type: \"text\" | \"textarea\" | \"number\" | \"rating\" | \"select\" | \"multiselect\" | \"boolean\";\n label?: string;\n help?: string;\n required?: boolean;\n /** select / multiselect choices */\n options?: string[];\n /** number bounds; rating scale 1–max (default 5) */\n min?: number;\n max?: number;\n /** prefilled, e.g. a proposed amount for the person to correct */\n default?: unknown;\n}\n/** The input a run started by a decision gets. */\nexport interface ApprovalInput<C = any, V = Record<string, any>> {\n approval: { id: string; title: string; decision: string; comment: string | null; values: V; decidedBy: string | null; decidedVia: string | null; decidedAt: number; requestedByRun: string | null };\n context: C;\n}\nexport class AgentRunError extends Error { run: Run }\nexport class StateConflict extends Error { key: string; version: number }\nexport function input<T = any>(fallback?: T): T;\nexport function output(value: unknown): void;\nexport function request(): WebhookRequest | null;\nexport function respond(body?: unknown, opts?: { status?: number; headers?: Record<string, string> }): void;\nexport function attempt(): number;\nexport function start(agent: string, input?: unknown, opts?: { idempotencyKey?: string; delaySec?: number; retries?: number }): Promise<string>;\nexport function get<O = any>(runId: string): Promise<Run<O>>;\nexport function run<O = any>(agent: string, input?: unknown, opts?: { timeoutMs?: number; pollMs?: number; check?: boolean; idempotencyKey?: string }): Promise<Run<O>>;\nexport const state: {\n get<T = any>(key: string, fallback?: T): Promise<T>;\n getVersioned<T = any>(key: string): Promise<{ value: T | null; version: number }>;\n set(key: string, value: unknown, opts?: { ifVersion?: number; ttlSec?: number }): Promise<number>;\n delete(key: string, opts?: { ifVersion?: number }): Promise<void>;\n list(prefix?: string, opts?: { values?: boolean }): Promise<Array<{ key: string; version: number; value?: any }>>;\n update<T = any>(key: string, fn: (old: T) => T | Promise<T>, fallback?: T, retries?: number): Promise<T>;\n};\nexport function step<T>(name: string, fn: () => T | Promise<T>, opts?: { ttlSec?: number }): Promise<T>;\nexport function requestApproval<C = any>(title: string, opts?: { details?: unknown; options?: string[]; approvers?: string[]; teams?: string[]; fields?: ApprovalField[]; then?: string | null; context?: C; expiresSec?: number }): Promise<Approval>;\nexport function getApproval(id: string): Promise<Approval>;\nexport function waitForApproval(id: string, opts?: { timeoutMs?: number; pollMs?: number }): Promise<Approval>;\nexport function openaiOptions(extra?: Record<string, unknown>): { baseURL: string; apiKey: string | undefined; defaultHeaders: Record<string, string> };\nexport function openai(extra?: Record<string, unknown>): Promise<import(\"openai\").default>;";
|
|
7
|
+
exports.jsPackage = "{\n \"name\": \"codiedev-agents\",\n \"version\": \"0.1.0\",\n \"description\": \"Codiedev agents SDK: this run's input and output, chaining, and a traced OpenAI client.\",\n \"type\": \"module\",\n \"main\": \"./index.mjs\",\n \"types\": \"./index.d.ts\",\n \"exports\": { \".\": { \"types\": \"./index.d.ts\", \"default\": \"./index.mjs\" } }\n}\n";
|
package/dist/commands/ask.js
CHANGED
|
@@ -3,9 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runAsk = runAsk;
|
|
4
4
|
const shared_1 = require("./shared");
|
|
5
5
|
function parseArgs(args) {
|
|
6
|
-
// Everything after the command, joined back into a question string. Allows
|
|
7
|
-
// both quoted and unquoted forms: `codiedev ask "why pg-bouncer?"` or
|
|
8
|
-
// `codiedev ask why did we drop pg-bouncer`.
|
|
9
6
|
const question = args
|
|
10
7
|
.filter((a) => !a.startsWith("--"))
|
|
11
8
|
.join(" ")
|
package/dist/commands/capture.js
CHANGED
|
@@ -123,7 +123,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
if (subcommand === "ignored") {
|
|
126
|
-
// `ignored` lists everything; `ignored <path>` answers "is this ignored?".
|
|
127
126
|
const query = args.slice(1).join(" ").trim();
|
|
128
127
|
if (query) {
|
|
129
128
|
reportCheck(query, current, deps);
|
|
@@ -139,7 +138,6 @@ async function runCaptureCommand(args, depsOverride = {}) {
|
|
|
139
138
|
deps.log(` ${value}`);
|
|
140
139
|
return;
|
|
141
140
|
}
|
|
142
|
-
// Back-compat: the earlier `codiedev capture check <path>` form.
|
|
143
141
|
if (subcommand === "check") {
|
|
144
142
|
reportCheck(requirePath(args), current, deps);
|
|
145
143
|
return;
|
|
@@ -100,7 +100,6 @@ async function runCreateTicket(args) {
|
|
|
100
100
|
await runBare(config, parsed.prompt);
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
// submit mode
|
|
104
103
|
if (!parsed.submitSource) {
|
|
105
104
|
console.error("--submit requires a path or '-' for stdin");
|
|
106
105
|
process.exit(1);
|
|
@@ -114,7 +113,6 @@ async function runCreateTicket(args) {
|
|
|
114
113
|
console.error(`Failed to read plan: ${err.message}`);
|
|
115
114
|
process.exit(1);
|
|
116
115
|
}
|
|
117
|
-
// Translate fullNames → IDs before submitting
|
|
118
116
|
let parsedPlan;
|
|
119
117
|
try {
|
|
120
118
|
parsedPlan = JSON.parse(raw);
|
|
@@ -141,14 +139,6 @@ async function runCreateTicket(args) {
|
|
|
141
139
|
}
|
|
142
140
|
await runSubmit(config, JSON.stringify(parsedPlan));
|
|
143
141
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Bare-mode entrypoint. Fetches the protocol body from the backend so we can
|
|
146
|
-
* iterate on it without shipping a new CLI, substitutes the user's prompt,
|
|
147
|
-
* and writes the result to stdout for the agent to follow. Hard-errors on
|
|
148
|
-
* fetch failure rather than falling back to a stale local copy — the whole
|
|
149
|
-
* point of moving the protocol server-side is that the freshest version is
|
|
150
|
-
* the only version.
|
|
151
|
-
*/
|
|
152
142
|
async function runBare(config, prompt) {
|
|
153
143
|
let res;
|
|
154
144
|
try {
|
package/dist/commands/doctor.js
CHANGED
|
@@ -81,12 +81,6 @@ function codexInstalled() {
|
|
|
81
81
|
function cursorInstalled() {
|
|
82
82
|
return fs.existsSync(path.join(os.homedir(), ".cursor"));
|
|
83
83
|
}
|
|
84
|
-
// `isCodiedevHookCommand` lives in ../detection alongside detectClaudeCode
|
|
85
|
-
// so utils.ts and doctor.ts share one definition (they used to drift —
|
|
86
|
-
// each had its own copy).
|
|
87
|
-
// Cursor's hooks.json schema is { hooks: { sessionEnd: [{ command, ... }] } }
|
|
88
|
-
// — flat array of objects with `command`, not the Claude/Codex nested
|
|
89
|
-
// `{ hooks: [{ command }] }` wrapper.
|
|
90
84
|
function hasCursorHook() {
|
|
91
85
|
try {
|
|
92
86
|
if (!fs.existsSync(CURSOR_HOOKS_PATH))
|
|
@@ -137,7 +131,6 @@ async function runDoctor(_args) {
|
|
|
137
131
|
const checks = [];
|
|
138
132
|
console.log("\nCodiedev doctor — checking your setup…\n");
|
|
139
133
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
140
|
-
// 1. Config file
|
|
141
134
|
const config = (0, utils_1.readConfig)();
|
|
142
135
|
if (!config) {
|
|
143
136
|
checks.push({
|
|
@@ -153,19 +146,12 @@ async function runDoctor(_args) {
|
|
|
153
146
|
status: "pass",
|
|
154
147
|
detail: `connected to ${config.companyName}`,
|
|
155
148
|
});
|
|
156
|
-
// 2. Token still valid + backend reachable (re-validate). The detail names
|
|
157
|
-
// the backend host so users (and their IT) can confirm which domain the CLI
|
|
158
|
-
// talks to — the same host capture uploads use, so this check exercises the
|
|
159
|
-
// exact TLS path a corporate proxy would intercept.
|
|
160
149
|
let backendHost = "the capture host";
|
|
161
150
|
try {
|
|
162
151
|
backendHost = new URL(config.backendUrl).host;
|
|
163
152
|
}
|
|
164
153
|
catch {
|
|
165
|
-
// malformed backendUrl in config — keep the generic placeholder
|
|
166
154
|
}
|
|
167
|
-
// Live tracked-repo list fetched from validateToken below. Null until (and
|
|
168
|
-
// unless) the backend answers; the checks fall back to `config.repos`.
|
|
169
155
|
let liveRepos = null;
|
|
170
156
|
try {
|
|
171
157
|
const res = await fetch(`${config.backendUrl}/api/cli/validateToken`, {
|
|
@@ -182,20 +168,13 @@ async function runDoctor(_args) {
|
|
|
182
168
|
status: "pass",
|
|
183
169
|
detail: backendHost,
|
|
184
170
|
});
|
|
185
|
-
// validateToken returns the company's CURRENT tracked repos. Capture
|
|
186
|
-
// them: the repo-resolution checks below must reflect what the SERVER
|
|
187
|
-
// will resolve at capture time (repoResolver.ts resolves against the
|
|
188
|
-
// live list), not the possibly-stale `config.repos` frozen at the last
|
|
189
|
-
// `connect`. A repo linked in the portal after that connect otherwise
|
|
190
|
-
// reports a false "isn't a tracked repo — link it in the portal" here
|
|
191
|
-
// even though sessions/MRs for it attribute fine server-side.
|
|
192
171
|
try {
|
|
193
172
|
const data = (await res.json());
|
|
194
173
|
if (Array.isArray(data.repos))
|
|
195
174
|
liveRepos = data.repos;
|
|
196
175
|
}
|
|
197
176
|
catch {
|
|
198
|
-
liveRepos = null;
|
|
177
|
+
liveRepos = null;
|
|
199
178
|
}
|
|
200
179
|
}
|
|
201
180
|
else {
|
|
@@ -207,9 +186,6 @@ async function runDoctor(_args) {
|
|
|
207
186
|
}
|
|
208
187
|
}
|
|
209
188
|
catch (err) {
|
|
210
|
-
// fetch buries the real TLS/socket error in .cause ("fetch failed" alone
|
|
211
|
-
// is undiagnosable); unwrap it and, when it's an SSL-inspection signature,
|
|
212
|
-
// say what actually fixes it.
|
|
213
189
|
const desc = (0, captureReadiness_1.describeFetchError)(err);
|
|
214
190
|
const tlsHint = (0, captureReadiness_1.tlsInterceptionHint)(desc, backendHost);
|
|
215
191
|
checks.push({
|
|
@@ -218,7 +194,6 @@ async function runDoctor(_args) {
|
|
|
218
194
|
detail: tlsHint ? `${desc} — ${tlsHint}` : `${desc} (host: ${backendHost})`,
|
|
219
195
|
});
|
|
220
196
|
}
|
|
221
|
-
// 3. Authenticated endpoint smoke test (inbox query)
|
|
222
197
|
try {
|
|
223
198
|
await (0, shared_1.apiRequest)("GET", "/api/cli/inbox", {
|
|
224
199
|
config,
|
|
@@ -236,38 +211,16 @@ async function runDoctor(_args) {
|
|
|
236
211
|
detail: err.message,
|
|
237
212
|
});
|
|
238
213
|
}
|
|
239
|
-
// 4. Tracked repos. Prefer the live list from validateToken over the cache:
|
|
240
|
-
// a repo linked after the last `connect` is tracked server-side but absent
|
|
241
|
-
// from `config.repos`, and resolving against the stale cache is exactly what
|
|
242
|
-
// produced the false "isn't a tracked repo — link it in the portal" warning.
|
|
243
|
-
// Treat an EMPTY live list as "no usable live data", NOT an authoritative
|
|
244
|
-
// zero: a transient permission glitch / backend hiccup can return `repos: []`,
|
|
245
|
-
// and both driving the checks from it AND healing the cache to empty would be
|
|
246
|
-
// wrong — the write would durably break every offline doctor run (and the
|
|
247
|
-
// capture hook's providedRepoId) until the next successful connect. So when
|
|
248
|
-
// live is empty, fall back to the cache and skip the write, exactly like
|
|
249
|
-
// backend-unreachable. A genuinely repo-less company also has an empty cache,
|
|
250
|
-
// so it still reports "0 repos" correctly.
|
|
251
214
|
const trackedRepos = liveRepos !== null && liveRepos.length > 0 ? liveRepos : (config.repos ?? []);
|
|
252
|
-
// Detect drift by CONTENT, not count: a same-size swap (one repo removed +
|
|
253
|
-
// another added between connects) or a repo whose path/URL changed while its
|
|
254
|
-
// id stayed stable (GitLab move/rename — exactly the SONIFI migration case)
|
|
255
|
-
// both leave the count unchanged. A length-only check would skip the heal and
|
|
256
|
-
// leave the capture hook's cache — and its advisory providedRepoId — stale.
|
|
257
215
|
const canonRepos = (rs) => JSON.stringify([...rs].map((r) => [r.repoId, r.url, r.fullName]).sort());
|
|
258
216
|
const cacheRefreshed = liveRepos !== null &&
|
|
259
217
|
liveRepos.length > 0 &&
|
|
260
218
|
canonRepos(liveRepos) !== canonRepos(config.repos ?? []);
|
|
261
|
-
// Heal the on-disk cache so the capture hook (which reads `config.repos` for
|
|
262
|
-
// the advisory providedRepoId) stops trailing the portal too. Best-effort:
|
|
263
|
-
// doctor must never fail because the config couldn't be rewritten.
|
|
264
219
|
if (cacheRefreshed && liveRepos) {
|
|
265
220
|
try {
|
|
266
221
|
(0, utils_1.writeConfig)({ ...config, repos: liveRepos, lastSynced: new Date().toISOString() });
|
|
267
222
|
}
|
|
268
223
|
catch {
|
|
269
|
-
// read-only home / permission issue — the in-memory list still drives
|
|
270
|
-
// the checks below; the user just re-syncs on the next `connect`.
|
|
271
224
|
}
|
|
272
225
|
}
|
|
273
226
|
const repoCount = trackedRepos.length;
|
|
@@ -278,11 +231,6 @@ async function runDoctor(_args) {
|
|
|
278
231
|
? `${repoCount} repo${repoCount === 1 ? "" : "s"}${cacheRefreshed ? " (refreshed from portal)" : ""}`
|
|
279
232
|
: "0 — link repos in the portal so sessions get captured",
|
|
280
233
|
});
|
|
281
|
-
// 4b. Capture readiness — the part that actually decides whether a session
|
|
282
|
-
// lands. doctor used to verify only that the hook was *installed*; these
|
|
283
|
-
// checks answer "from this directory, would a session capture, and did my
|
|
284
|
-
// last one?" — the blind spot behind lean-container / submodule / no-remote
|
|
285
|
-
// zeros.
|
|
286
234
|
const cwd = process.cwd();
|
|
287
235
|
const readiness = (0, captureReadiness_1.assessCaptureReadiness)(cwd, trackedRepos, {
|
|
288
236
|
exec: (cmd, dir) => (0, child_process_1.execSync)(cmd, { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }).toString(),
|
|
@@ -291,11 +239,6 @@ async function runDoctor(_args) {
|
|
|
291
239
|
getBranch: utils_1.getGitBranch,
|
|
292
240
|
getHeadSha: utils_1.getGitHeadSha,
|
|
293
241
|
});
|
|
294
|
-
// Workspace-root mode: the cwd isn't a repo itself, but git repos sit
|
|
295
|
-
// directly under it (one `.devcontainer/` at a parent folder of several
|
|
296
|
-
// cloned repos). Capture tags the sub-repo a session edits, so report that
|
|
297
|
-
// instead of flatly warning "not a work tree / no remote" — the exact output
|
|
298
|
-
// that made working workspace-root setups read as broken.
|
|
299
242
|
const workspaceScan = readiness.gitOnPath && !readiness.insideWorkTree
|
|
300
243
|
? (0, captureReadiness_1.assessWorkspaceRepos)(cwd, trackedRepos, {
|
|
301
244
|
listChildDirs: (dir) => fs
|
|
@@ -310,8 +253,6 @@ async function runDoctor(_args) {
|
|
|
310
253
|
const workspaceMode = workspaceScan.repoCount > 0;
|
|
311
254
|
checks.push({
|
|
312
255
|
name: "git on PATH",
|
|
313
|
-
// A warning, not a failure: sessions still capture without git, they just
|
|
314
|
-
// aren't tagged to a repo. Non-engineer installs often skip git entirely.
|
|
315
256
|
status: readiness.gitOnPath ? "pass" : "warn",
|
|
316
257
|
detail: readiness.gitOnPath
|
|
317
258
|
? undefined
|
|
@@ -348,9 +289,6 @@ async function runDoctor(_args) {
|
|
|
348
289
|
? "branch/sha resolve from the sub-repo each session writes to"
|
|
349
290
|
: "no branch/sha resolved — session→merge attribution will be weaker",
|
|
350
291
|
});
|
|
351
|
-
// 4c. The payoff: did the last session actually land? Ask the server for
|
|
352
|
-
// portal truth (does the row exist for this user?), falling back to the local
|
|
353
|
-
// 2xx breadcrumb if the endpoint is unreachable or predates this CLI/backend.
|
|
354
292
|
const localMarker = (0, utils_1.readLastCapture)();
|
|
355
293
|
let server = null;
|
|
356
294
|
try {
|
|
@@ -371,7 +309,7 @@ async function runDoctor(_args) {
|
|
|
371
309
|
server = (await res.json());
|
|
372
310
|
}
|
|
373
311
|
catch {
|
|
374
|
-
server = null;
|
|
312
|
+
server = null;
|
|
375
313
|
}
|
|
376
314
|
const lastCapture = (0, captureReadiness_1.summarizeServerCapture)(server, localMarker, Date.now());
|
|
377
315
|
checks.push({
|
|
@@ -379,7 +317,6 @@ async function runDoctor(_args) {
|
|
|
379
317
|
status: lastCapture.status,
|
|
380
318
|
detail: lastCapture.detail,
|
|
381
319
|
});
|
|
382
|
-
// 4d. Capture-log activity: did recent uploads succeed? Are there pending jobs?
|
|
383
320
|
const logTail = (0, captureLog_1.readCaptureLogTail)(40);
|
|
384
321
|
let pendingJobCount = 0;
|
|
385
322
|
try {
|
|
@@ -396,17 +333,12 @@ async function runDoctor(_args) {
|
|
|
396
333
|
status: captureActivity.status,
|
|
397
334
|
detail: captureActivity.detail,
|
|
398
335
|
});
|
|
399
|
-
// 5. Claude Code setup (if present)
|
|
400
336
|
if ((0, utils_1.claudeCodeInstalled)()) {
|
|
401
337
|
checks.push({
|
|
402
338
|
name: "Claude Code detected",
|
|
403
339
|
status: "pass",
|
|
404
340
|
detail: (0, harnessDirs_1.displayPath)(CLAUDE_DIR),
|
|
405
341
|
});
|
|
406
|
-
// A hook in settings.json only fires if Claude Code actually reads this
|
|
407
|
-
// dir. If it holds nothing Claude Code writes on use, it's running from
|
|
408
|
-
// somewhere else (CLAUDE_CONFIG_DIR unset in this shell, another user,
|
|
409
|
-
// the host vs. a container) and the ✓s below are hollow.
|
|
410
342
|
checks.push(configDirInUseCheck("Claude Code", CLAUDE_DIR, (0, harnessDirs_1.claudeConfigDirInUse)(CLAUDE_DIR), "CLAUDE_CONFIG_DIR"));
|
|
411
343
|
checks.push({
|
|
412
344
|
name: "Claude Code SessionEnd hook",
|
|
@@ -430,7 +362,6 @@ async function runDoctor(_args) {
|
|
|
430
362
|
detail: "not installed on this machine",
|
|
431
363
|
});
|
|
432
364
|
}
|
|
433
|
-
// 6. Codex setup (if present)
|
|
434
365
|
if (codexInstalled()) {
|
|
435
366
|
checks.push({
|
|
436
367
|
name: "Codex detected",
|
|
@@ -460,7 +391,6 @@ async function runDoctor(_args) {
|
|
|
460
391
|
detail: "not installed on this machine",
|
|
461
392
|
});
|
|
462
393
|
}
|
|
463
|
-
// 7. Cursor setup (if present)
|
|
464
394
|
if (cursorInstalled()) {
|
|
465
395
|
checks.push({
|
|
466
396
|
name: "Cursor detected",
|
package/dist/commands/evals.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// `codiedev evals report <run.json> [--harness name]`
|
|
3
|
-
// `codiedev evals last <subject> [--json]`
|
|
4
|
-
//
|
|
5
|
-
// Evals are experiments (a question about a subject: an agent, or a workload
|
|
6
|
-
// like session-summaries) and the runs that answer them, one per variant. The
|
|
7
|
-
// eval runs wherever it runs (a script, a coding agent); `report` files the
|
|
8
|
-
// scores under the experiment in Codiedev → Traces → Evals, shared with the
|
|
9
|
-
// team. `last` is where to start the next one: the latest experiment on the
|
|
10
|
-
// subject, its runs, and the champion the subject runs today.
|
|
11
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
3
|
if (k2 === undefined) k2 = k;
|
|
13
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -67,6 +58,7 @@ codiedev evals — experiments and their runs, shared with the team
|
|
|
67
58
|
"provider": "openai", "reasoningEffort": "medium", "prompt": "summary_extract_v2",
|
|
68
59
|
"gitSha": "…", "isControl": false, "judgeModel": "anthropic/claude-sonnet-5.5",
|
|
69
60
|
"casesTotal": 30, "casesScored": 30, "errors": 0,
|
|
61
|
+
"scoringRevision": 1, // required once experiment scoring rules are defined
|
|
70
62
|
"score": 3.85, "metrics": { "fidelity": 3.6 },
|
|
71
63
|
"costPerCaseUsd": 0.026, "latencyMsAvg": 21000,
|
|
72
64
|
"wins": 20, "losses": 8, "ties": 2, "note": "…",
|
|
@@ -74,6 +66,8 @@ codiedev evals — experiments and their runs, shared with the team
|
|
|
74
66
|
}
|
|
75
67
|
}
|
|
76
68
|
|
|
69
|
+
Read scoring rules and their current revision with: codiedev evals last <subject> --json
|
|
70
|
+
|
|
77
71
|
Scores and notes only: never put prompts, transcripts or outputs in it.
|
|
78
72
|
`.trim();
|
|
79
73
|
function flag(args, i, name) {
|
package/dist/commands/inbox.js
CHANGED
|
@@ -70,8 +70,6 @@ async function runRead(args) {
|
|
|
70
70
|
const config = (0, shared_1.requireConfig)();
|
|
71
71
|
const start = Date.now();
|
|
72
72
|
try {
|
|
73
|
-
// First, fetch inbox so we can show the full body (markRead endpoint
|
|
74
|
-
// doesn't return content).
|
|
75
73
|
const listRes = await (0, shared_1.apiRequest)("GET", "/api/cli/inbox", {
|
|
76
74
|
config,
|
|
77
75
|
query: { limit: 100 },
|
package/dist/commands/library.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// `codiedev library [--scope mine|shared|all] [--kind doc|skill] [--tag X]
|
|
3
|
-
// [--type spec|...] [--limit N]` — list artifacts in your Codiedev workspace.
|
|
4
|
-
// Server applies companyId scoping; --scope filters within that.
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.runLibrary = runLibrary;
|
|
7
4
|
const shared_1 = require("./shared");
|
package/dist/commands/pack.js
CHANGED
|
@@ -59,10 +59,7 @@ const harnesses_2 = require("../packs/harnesses");
|
|
|
59
59
|
const os = __importStar(require("os"));
|
|
60
60
|
const utils_1 = require("../utils");
|
|
61
61
|
const shared_1 = require("./shared");
|
|
62
|
-
// --- CI (pack-scoped `cdvci_` keys) ------------------------------------------
|
|
63
62
|
exports.DEFAULT_BACKEND_URL = "https://cli.codiedev.com";
|
|
64
|
-
/** CODIEDEV_TOKEN alone is enough: no ~/.codiedev/config.json needed (CI).
|
|
65
|
-
* CODIEDEV_URL defaults to production. Otherwise the connected config. */
|
|
66
63
|
function resolvePackConfig(env = process.env) {
|
|
67
64
|
const token = env.CODIEDEV_TOKEN?.trim();
|
|
68
65
|
if (token) {
|
|
@@ -78,8 +75,6 @@ function resolvePackConfig(env = process.env) {
|
|
|
78
75
|
const config = (0, utils_1.readConfig)();
|
|
79
76
|
return config ? (0, shared_1.applyConfigEnvOverrides)(config) : null;
|
|
80
77
|
}
|
|
81
|
-
/** In CI, `--harness` defaults to claude when it's on PATH; anything else is
|
|
82
|
-
* ambiguous on a build box, so say what was found. */
|
|
83
78
|
function ciHarnesses(args, env, home) {
|
|
84
79
|
if (args.harnesses?.length)
|
|
85
80
|
return args.harnesses;
|
|
@@ -223,8 +218,6 @@ async function runInstall(config, args, overrides = {}) {
|
|
|
223
218
|
const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
|
|
224
219
|
const harnesses = ci && !args.dryRun ? ciHarnesses(args, overrides.env ?? process.env, overrides.home) : args.harnesses;
|
|
225
220
|
const listed = args.slug ? [args.slug] : (await listPacks(config)).map((p) => p.slug);
|
|
226
|
-
// With no slug, also re-check every locally installed pack: one that was
|
|
227
|
-
// unassigned or archived 404s and must be removed locally. Not in CI: no pruning.
|
|
228
221
|
const slugs = args.slug || ci ? listed : [...new Set([...listed, ...Object.keys(registry.packs)])];
|
|
229
222
|
const manifests = [];
|
|
230
223
|
const revoked = [];
|
|
@@ -241,7 +234,6 @@ async function runInstall(config, args, overrides = {}) {
|
|
|
241
234
|
revoked.push(slug);
|
|
242
235
|
else if (args.slug)
|
|
243
236
|
throw new Error(`Pack not found or not assigned to your team: ${slug}`);
|
|
244
|
-
// A listed pack that 404s a moment later (race with an unassign) is skipped.
|
|
245
237
|
}
|
|
246
238
|
}
|
|
247
239
|
let installed = manifests.length
|
|
@@ -249,8 +241,6 @@ async function runInstall(config, args, overrides = {}) {
|
|
|
249
241
|
: [];
|
|
250
242
|
let removed = [];
|
|
251
243
|
if (!args.dryRun && !ci) {
|
|
252
|
-
// A 404 on the installed report means the pack stopped applying between
|
|
253
|
-
// the manifest fetch and now: treat it exactly like a revoked pack.
|
|
254
244
|
const lateRevoked = await reportInstalled(config, installed);
|
|
255
245
|
installed = installed.filter((p) => !lateRevoked.includes(p.slug));
|
|
256
246
|
removed = await removePacks(config, [...revoked, ...lateRevoked], overrides);
|
|
@@ -285,11 +275,7 @@ async function runInstall(config, args, overrides = {}) {
|
|
|
285
275
|
for (const line of (0, install_1.formatUninstallLines)(removed))
|
|
286
276
|
console.log(line);
|
|
287
277
|
}
|
|
288
|
-
// Exit non-zero when a pack reached no tool because of a failure, or the
|
|
289
|
-
// pack explicitly asked for is no longer available.
|
|
290
278
|
const failed = installed.some((p) => p.results.some((r) => r.status === "failed") && !p.results.some((r) => r.status === "ok"));
|
|
291
|
-
// CI is strict: every requested tool must have taken every pack, and there
|
|
292
|
-
// must have been a pack to install at all.
|
|
293
279
|
const ciFailed = ci && !args.dryRun && (installed.length === 0 || installed.some((p) => p.results.some((r) => r.status !== "ok")));
|
|
294
280
|
if (ci && !args.dryRun && installed.length === 0)
|
|
295
281
|
console.error("No pack is visible to this key.");
|
|
@@ -297,8 +283,6 @@ async function runInstall(config, args, overrides = {}) {
|
|
|
297
283
|
process.exitCode = 1;
|
|
298
284
|
return run;
|
|
299
285
|
}
|
|
300
|
-
/** POST each install. Returns slugs the server answered 404 for (revoked);
|
|
301
|
-
* any other failure is only a warning. */
|
|
302
286
|
async function reportInstalled(config, results) {
|
|
303
287
|
const revoked = [];
|
|
304
288
|
for (const pack of results) {
|
|
@@ -317,7 +301,6 @@ async function reportInstalled(config, results) {
|
|
|
317
301
|
}
|
|
318
302
|
return revoked;
|
|
319
303
|
}
|
|
320
|
-
/** Uninstall packs locally, then tell the server (best-effort, idempotent). */
|
|
321
304
|
async function removePacks(config, slugs, overrides) {
|
|
322
305
|
if (slugs.length === 0)
|
|
323
306
|
return [];
|
|
@@ -365,7 +348,6 @@ async function runStatus(config, json, overrides = {}, options = {}) {
|
|
|
365
348
|
}
|
|
366
349
|
rows.push({ slug, ...entry, upToDate, latestVersion, latestChannel });
|
|
367
350
|
}
|
|
368
|
-
// --no-prune (background check when auto-update is off) and CI: report only.
|
|
369
351
|
const ci = (0, sync_1.isPackCiMode)(overrides.env ?? process.env);
|
|
370
352
|
const removed = options.prune === false || ci ? [] : await removePacks(config, revoked, overrides);
|
|
371
353
|
if (available && !ci) {
|
|
@@ -414,9 +396,7 @@ async function runPack(args) {
|
|
|
414
396
|
}
|
|
415
397
|
const config = resolvePackConfig() ?? (0, shared_1.requireConfig)();
|
|
416
398
|
const ci = (0, sync_1.isPackCiMode)();
|
|
417
|
-
// --quiet (the background auto-update): no output at all.
|
|
418
399
|
const restore = parsed.quiet ? silenceConsole() : () => { };
|
|
419
|
-
// CI keys can only read packs: no telemetry POST either.
|
|
420
400
|
const track = ci ? (_c, _t, fn) => fn() : shared_1.withTelemetry;
|
|
421
401
|
try {
|
|
422
402
|
await track(config, `codiedev_pack_${parsed.sub === "ls" ? "list" : parsed.sub}`, async () => {
|
|
@@ -449,13 +429,10 @@ function silenceConsole() {
|
|
|
449
429
|
console.error = error;
|
|
450
430
|
};
|
|
451
431
|
}
|
|
452
|
-
// --- pack export / apply ---------------------------------------------------
|
|
453
432
|
function gitIn(dir, args) {
|
|
454
433
|
const r = (0, child_process_1.spawnSync)("git", args, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
455
434
|
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? (r.error?.message ?? "") };
|
|
456
435
|
}
|
|
457
|
-
/** Init (if needed) and commit everything under `dir`. Uses the user's git
|
|
458
|
-
* identity when configured, a neutral Codiedev one otherwise (fresh CI). */
|
|
459
436
|
function commitExport(dir, message) {
|
|
460
437
|
if (!fs.existsSync(path.join(dir, ".git"))) {
|
|
461
438
|
const init = gitIn(dir, ["init", "-q"]);
|
|
@@ -491,9 +468,6 @@ function readMarketplaceEntries(dir) {
|
|
|
491
468
|
return [];
|
|
492
469
|
}
|
|
493
470
|
}
|
|
494
|
-
/** Write a CI-committable copy of the pack: `<out>/plugins/<slug>/…` plus
|
|
495
|
-
* `<out>/.claude-plugin/marketplace.json` (existing entries for other packs
|
|
496
|
-
* in the same folder are kept). */
|
|
497
471
|
function exportPluginFolder(out, manifest) {
|
|
498
472
|
fs.mkdirSync(out, { recursive: true });
|
|
499
473
|
const dir = (0, store_1.writePluginDir)(out, manifest);
|
|
@@ -522,7 +496,6 @@ async function runExport(config, args) {
|
|
|
522
496
|
if (typeof res.markdown !== "string")
|
|
523
497
|
throw new Error("Server returned no markdown");
|
|
524
498
|
fs.mkdirSync(out, { recursive: true });
|
|
525
|
-
// <slug>.pack.md so several packs can share one folder.
|
|
526
499
|
written = path.join(out, `${args.slug}.pack.md`);
|
|
527
500
|
(0, store_1.writeFileAtomic)(written, res.markdown.endsWith("\n") ? res.markdown : `${res.markdown}\n`);
|
|
528
501
|
message = `codiedev pack export ${args.slug} (${args.slug}.pack.md)`;
|
|
@@ -576,8 +549,6 @@ async function runApply(config, args) {
|
|
|
576
549
|
res = await (0, shared_1.apiRequest)("POST", "/api/cli/packs/apply", { config, body: { markdown } });
|
|
577
550
|
}
|
|
578
551
|
catch (error) {
|
|
579
|
-
// The server reports pack.md problems with line numbers; show them as-is.
|
|
580
|
-
// The server's 400 is already "pack.md line N: …" — print it verbatim.
|
|
581
552
|
const e = error;
|
|
582
553
|
if (e.status === 403)
|
|
583
554
|
throw new Error(`You can't edit this pack (${e.message})`);
|
package/dist/commands/ping.js
CHANGED
|
@@ -23,8 +23,6 @@ function parseArgs(args) {
|
|
|
23
23
|
process.exit(1);
|
|
24
24
|
}
|
|
25
25
|
to = positional[0];
|
|
26
|
-
// Join remaining positionals in case the shell split the message on spaces
|
|
27
|
-
// despite quoting (happens with some escape sequences).
|
|
28
26
|
const body = (0, shared_1.stripQuotes)(positional.slice(1).join(" "));
|
|
29
27
|
return { to, body, subjectKey };
|
|
30
28
|
}
|
package/dist/commands/pull.js
CHANGED
|
@@ -71,10 +71,6 @@ function parseArgs(args) {
|
|
|
71
71
|
}
|
|
72
72
|
return { key, version, out };
|
|
73
73
|
}
|
|
74
|
-
// The backend stores skills under extension-bearing keys (e.g. "pr-push.md"),
|
|
75
|
-
// and lookup is exact-match, so a bare "pr-push" 404s. Make pull forgiving: try
|
|
76
|
-
// the key as given, then a ".md" fallback when it has no extension, so the
|
|
77
|
-
// intuitive bare name (and the hook's suggested key) both resolve.
|
|
78
74
|
function candidateKeys(key) {
|
|
79
75
|
if (/\.[a-z0-9]+$/i.test(key))
|
|
80
76
|
return [key];
|
|
@@ -97,16 +93,12 @@ async function runPull(args) {
|
|
|
97
93
|
}
|
|
98
94
|
catch (err) {
|
|
99
95
|
const e = err;
|
|
100
|
-
// A 404 on a non-final candidate is retryable (fall through to ".md");
|
|
101
|
-
// anything else — or the last candidate — propagates to the handler.
|
|
102
96
|
if (e.status === 404 && i < keys.length - 1)
|
|
103
97
|
continue;
|
|
104
98
|
throw err;
|
|
105
99
|
}
|
|
106
100
|
}
|
|
107
101
|
const a = res.artifact;
|
|
108
|
-
// A skill carries its identity onto disk so its runs attribute exactly
|
|
109
|
-
// (see skillMarker.ts). Docs are written verbatim.
|
|
110
102
|
const isSkill = a.kind ? a.kind === "skill" : (0, skillMarker_1.looksLikeSkill)(a.markdown);
|
|
111
103
|
const markdown = isSkill ? (0, skillMarker_1.stampSkillMarker)(a.markdown, a._id) : a.markdown;
|
|
112
104
|
(0, shared_1.recordAgentEvent)(config, {
|
|
@@ -123,7 +115,6 @@ async function runPull(args) {
|
|
|
123
115
|
console.log(` updated: ${(0, shared_1.timeAgo)(a.updatedAt ?? a.createdAt)}`);
|
|
124
116
|
}
|
|
125
117
|
else {
|
|
126
|
-
// Stream to stdout — preserves shell pipes like `codiedev pull spec-214.md | less`.
|
|
127
118
|
process.stdout.write(markdown);
|
|
128
119
|
if (!markdown.endsWith("\n"))
|
|
129
120
|
process.stdout.write("\n");
|
package/dist/commands/push.js
CHANGED
|
@@ -107,10 +107,6 @@ function parseArgs(args) {
|
|
|
107
107
|
console.error(`Invalid --kind. Valid kinds: ${[...VALID_KINDS].join(", ")}`);
|
|
108
108
|
process.exit(1);
|
|
109
109
|
}
|
|
110
|
-
// The override key is stored verbatim by the server (docs key off it, and a
|
|
111
|
-
// path-y value like "specs/feature-a/plan.md" populates folderPath). Strip a
|
|
112
|
-
// leading slash so it can't be confused with an absolute path, and reject an
|
|
113
|
-
// empty value — but otherwise allow slashes so callers can namespace.
|
|
114
110
|
if (as !== undefined) {
|
|
115
111
|
as = as.replace(/^\/+/, "").trim();
|
|
116
112
|
if (!as) {
|
|
@@ -121,7 +117,6 @@ function parseArgs(args) {
|
|
|
121
117
|
return { file, type, kind, tags, force, as, private: priv };
|
|
122
118
|
}
|
|
123
119
|
async function pushFile(config, opts) {
|
|
124
|
-
// Never upload another artifact's identity marker (skillMarker.ts).
|
|
125
120
|
const markdown = (0, skillMarker_1.stripSkillMarker)(fs.readFileSync(opts.absPath, "utf8"));
|
|
126
121
|
const gitRemoteUrl = (0, utils_1.getGitRemoteUrl)(process.cwd()) ?? undefined;
|
|
127
122
|
return shared.apiRequest("POST", "/api/cli/push", {
|
|
@@ -155,10 +150,6 @@ async function runPush(args) {
|
|
|
155
150
|
console.error(`File is empty: ${absolute}`);
|
|
156
151
|
process.exit(1);
|
|
157
152
|
}
|
|
158
|
-
// Stored key defaults to the basename (legacy behavior). `--as` overrides it
|
|
159
|
-
// so callers can namespace generically-named files — e.g. Spec Kit emits a
|
|
160
|
-
// `plan.md`/`spec.md` per feature, which would otherwise all collide on one
|
|
161
|
-
// key and silently version over each other.
|
|
162
153
|
const filename = as ?? path.basename(absolute);
|
|
163
154
|
const start = Date.now();
|
|
164
155
|
try {
|
|
@@ -179,9 +170,6 @@ async function runPush(args) {
|
|
|
179
170
|
const tagSummary = res.tags && res.tags.length > 0 ? ` tags=[${res.tags.join(", ")}]` : "";
|
|
180
171
|
console.log(`✓ Pushed ${filename} (kind=${res.kind}${tagSummary}).`);
|
|
181
172
|
console.log(` Visible under: ${res.destination}`);
|
|
182
|
-
// Echo the *resolved* key the server stored, not the local filename —
|
|
183
|
-
// skills are keyed by their frontmatter name (so two `SKILL.md` files
|
|
184
|
-
// don't collide), so the stored key can differ from what was pushed.
|
|
185
173
|
if (res.key && res.key !== filename) {
|
|
186
174
|
console.log(` Stored as: ${res.key}`);
|
|
187
175
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// `codiedev search "<query>"` — semantic + keyword search across the org's
|
|
3
|
-
// captured sessions, artifacts, and skills. Hits are filtered by companyId
|
|
4
|
-
// server-side, so a single search surfaces results from every teammate's
|
|
5
|
-
// prior sessions in the same workspace.
|
|
6
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
3
|
exports.runSearch = runSearch;
|
|
8
4
|
const shared_1 = require("./shared");
|