run402 4.67.2 → 4.68.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.
package/README.md CHANGED
@@ -70,7 +70,7 @@ run402 repos delete --project prj_xyz --force # refuses without --force while th
70
70
 
71
71
  A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. Every mutating `repos` verb (`create`, `rename`, `delete`, `snapshot`, `policy`, `mirror`, `gc`, `handoff`, `resume`) is CLI/OpenClaw-only by design — no MCP tool exists or will exist for them (one-shot recovery receipts, immutable generations, irreversible delete, single-use bearer keys). Three READ-ONLY tools do exist — `repos_view`, `repos_list_heads`, `repos_fsck` — teaching only `repos` spellings. See `run402 repos --help` for the full tiered surface (common: `create`/`view`/`list`; occasional: `snapshot`/`mirror`/`recover`/`handoff`/`resume`; lifecycle: `rename`/`delete`; maintenance: `fsck`/`gc`/`access`/`policy`), and the CLI reference's `repos` section for the terminal-loss statement and the progressive backup warning.
72
72
 
73
- **Handoff / resume.** `run402 repos handoff [--note-file <path>]` captures the actual working tree (staged/unstaged/untracked, like `git stash push -u`) into the vault and mints a single-use bearer key (`kgh1_…`, printed to stdout exactly once); `run402 repos resume <kgh1_…>` claims it on another machine, clones fresh, and reapplies the exact dirty state with `git stash apply --index`. No shared keystore, no shared allowance — the key is the whole handoff. Sensitive untracked files (`.env`, `*.pem`, SSH/AWS/GPG dirs, …) are excluded from capture by default (`--include-sensitive <glob>` re-admits one). See the reference `run402.com/llms-cli.txt` for the full note-schema and flag reference.
73
+ **Handoff / resume.** `run402 repos handoff [--note-file <path>]` captures the actual working tree (staged/unstaged/untracked, like `git stash push -u`) into the vault and mints a single-use bearer key (`kgh1_…`, printed to stdout exactly once); `run402 repos resume <kgh1_…>` claims it on another machine, clones fresh, and reapplies the exact dirty state with `git stash apply --index`. No shared keystore, no shared allowance — the key is the whole handoff. On a wallet with no active tier, `resume` first folds the same cold-start chain `create` does (allowance → faucet → one x402 prototype payment); `--no-init` opts out, and the claim itself never waits on it. Sensitive untracked files (`.env`, `*.pem`, SSH/AWS/GPG dirs, …) are excluded from capture by default (`--include-sensitive <glob>` re-admits one). See the reference `run402.com/llms-cli.txt` for the full note-schema and flag reference.
74
74
 
75
75
  **Named addressing.** `run402 org slug <slug>` (owner-only, small one-time fee) claims an org's globally-unique, address-form slug, after which `run402::<slug>/<name>` addresses any repo under it — `git push` to a name that doesn't exist yet push-to-creates it. `run402 repos rename <name> [--project <id>]` claims the per-org-unique `<name>` half explicitly (no fee); `repos create` claims one automatically, best-effort, when the org already has a slug. Also CLI/SDK-only — no MCP tool.
76
76
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "surface_version": "4.67.2",
2
+ "surface_version": "4.68.0",
3
3
  "verbs": [
4
4
  "repos create",
5
5
  "repos list",
package/lib/repos.mjs CHANGED
@@ -26,7 +26,7 @@ import { basename, join } from "node:path";
26
26
  import { getSdk } from "./sdk.mjs";
27
27
  import { reportSdkError, fail } from "./sdk-errors.mjs";
28
28
  import { withAutoApprove } from "./operator.mjs";
29
- import { allowanceAuthHeaders, isCoreApiTarget, resolveProjectId } from "./config.mjs";
29
+ import { allowanceAuthHeaders, isCoreApiTarget, readAllowance, resolveProjectId } from "./config.mjs";
30
30
  import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
31
31
  import { resolveOrgId, resolveOwningOrgId } from "./org-context.mjs";
32
32
  import { resolveGitvaultTarget } from "./gitvault-target.mjs";
@@ -74,7 +74,7 @@ Handoff (pass a working tree to another agent):
74
74
  run402 repos handoff [--project <id>] [--repo <repo_id>] [--ttl <seconds>] [--role <role>]
75
75
  [--include-sensitive <glob>]... [--note-file <path>] [--json] [--list] [--revoke <handoff_id>]
76
76
  (the Handoff Note is JSON piped on stdin when not using --note-file)
77
- run402 repos resume <kgh1_…|--key-stdin> [--to <dir>] [--json]
77
+ run402 repos resume <kgh1_…|--key-stdin> [--to <dir>] [--no-init] [--json]
78
78
 
79
79
  Occasional:
80
80
  run402 repos snapshot [--project <id>] [--repo <repo_id>] [--message <text>] [--checkpoint] [--dry-run] [--allow-dirty] [--manifest-out <path>]
@@ -151,9 +151,14 @@ Subcommands:
151
151
  <glob>\` re-admits a named untracked path the sensitive denylist
152
152
  would otherwise exclude (repeatable). \`--list\`/\`--revoke
153
153
  <handoff_id>\` read/revoke instead of minting.
154
- resume Resume a Handoff Key on ANY machine: parses the key, claims it
155
- with THIS machine's own wallet (creating the allowance file if
156
- absent no payment, ever), clones the vault at the base HEAD
154
+ resume Resume a Handoff Key on ANY machine: parses the key; on a wallet
155
+ with no active tier first folds the same cold-start chain
156
+ \`create\` does (allowance faucet one x402 prototype payment,
157
+ each step announced) so the resumed agent arrives as a paid-up
158
+ run402 wallet of its own — \`--no-init\` opts out, and because the
159
+ claim itself needs no tier a chain failure is reported (never
160
+ blocks the resume); then claims the key with THIS machine's own
161
+ wallet, clones the vault at the base HEAD
157
162
  into \`--to <dir>\` (default: the vault's name), applies the
158
163
  stash-shaped checkpoint (staged/unstaged/deleted/untracked
159
164
  restored distinctly), pins the repo/org/room into the checkout's
@@ -1668,6 +1673,48 @@ async function handoff(args) {
1668
1673
  }
1669
1674
  }
1670
1675
 
1676
+ /**
1677
+ * Whether this wallet needs the cold-start chain before a resume: no
1678
+ * allowance file yet (a fresh machine — the viral case), or an allowance
1679
+ * whose org holds no active tier. An unreachable tier status reads as
1680
+ * "no" — the claim needs no tier, and a resume must never wait on a status
1681
+ * read.
1682
+ */
1683
+ async function resumeNeedsColdStart(sdk) {
1684
+ if (!readAllowance()) return true;
1685
+ try {
1686
+ const status = await sdk.tier.status();
1687
+ return status?.active === false;
1688
+ } catch {
1689
+ return false;
1690
+ }
1691
+ }
1692
+
1693
+ /**
1694
+ * `resume`'s cold-start fold: the SAME chain `create` runs on
1695
+ * NO_ACTIVE_TIER (`cold-start.mjs`), announced step by step on stderr.
1696
+ * Never throws — a failure is returned as `{ error, next_action }` so the
1697
+ * caller carries it in the result and proceeds with the claim.
1698
+ */
1699
+ async function foldColdStartForResume(sdk) {
1700
+ if (!(await resumeNeedsColdStart(sdk))) return { performed: false, skipped: "tier_active" };
1701
+ console.error("no active tier — folding the cold-start chain (allowance -> faucet -> prototype tier) before the claim");
1702
+ try {
1703
+ const { foldColdStartChain } = await import("./cold-start.mjs");
1704
+ const chain = await foldColdStartChain((line) => console.error(` ${line}`));
1705
+ return { performed: true, ...chain };
1706
+ } catch (err) {
1707
+ const code = err?.body?.code ?? err?.code ?? null;
1708
+ const message = err?.body?.message ?? err?.message ?? String(err);
1709
+ console.error(`cold-start chain failed (${code ?? "error"}: ${message}) — continuing with the claim; run \`run402 tier set prototype\` afterwards`);
1710
+ return {
1711
+ performed: false,
1712
+ error: { code, message },
1713
+ next_action: { type: "renew_tier", command: "run402 tier set prototype", why: "The resume proceeded without a tier of your own; the perpetual prototype tier is what lets this wallet create and deploy projects." },
1714
+ };
1715
+ }
1716
+ }
1717
+
1671
1718
  /** `kygit.handoff-note.v1` rendered as Markdown — `resume`'s default (non-`--json`) rendering. */
1672
1719
  function renderHandoffNoteMarkdown(note) {
1673
1720
  if (!note) return null;
@@ -1708,7 +1755,7 @@ function renderHandoffNoteMarkdown(note) {
1708
1755
 
1709
1756
  async function resume(args) {
1710
1757
  const a = normalizeArgv(args);
1711
- assertKnownFlags(a, [...RESUME_VALUE_FLAGS, "--key-stdin", "--json", "-v", "--verbose", "--help", "-h"], RESUME_VALUE_FLAGS);
1758
+ assertKnownFlags(a, [...RESUME_VALUE_FLAGS, "--key-stdin", "--no-init", "--json", "-v", "--verbose", "--help", "-h"], RESUME_VALUE_FLAGS);
1712
1759
  const sdk = getSdk();
1713
1760
  const keyStdin = a.includes("--key-stdin");
1714
1761
  const to = flagValue(a, "--to");
@@ -1718,10 +1765,22 @@ async function resume(args) {
1718
1765
  key = (await readStdinText()).trim();
1719
1766
  if (!key) fail({ code: "BAD_USAGE", message: "Missing the Handoff Key on stdin.", hint: "Pipe the kgh1_… key, or pass it as a positional argument." });
1720
1767
  }
1768
+ // Decided 2026-09-02 (Tal; amends kygit-handoff design D5's "none on
1769
+ // resume"): a resumed agent is a NEW run402 wallet, and the loop is the
1770
+ // point — so on a wallet with no active tier `resume` folds the same
1771
+ // cold-start chain `create` does (allowance → faucet → one x402
1772
+ // prototype payment, announced) BEFORE the claim. The claim itself needs
1773
+ // no tier, so the chain is never allowed to block a resume: a faucet
1774
+ // throttle or payment failure is reported on stderr, carried in the
1775
+ // result as `cold_start.error` with a `renew_tier` next action, and the
1776
+ // claim proceeds (the SDK still creates the bare wallet it needs).
1777
+ // `--no-init` opts out entirely.
1778
+ const coldStart = a.includes("--no-init") ? { performed: false, skipped: "no_init" } : await foldColdStartForResume(sdk);
1721
1779
  try {
1722
1780
  const result = await sdk.gitvault.resume({ key, ...(to != null ? { to } : {}), onLine: (line) => console.error(line) });
1781
+ if (coldStart.next_action) result.next_actions = [...(result.next_actions ?? []), coldStart.next_action];
1723
1782
  if (a.includes("--json")) {
1724
- printJson(sdk, result);
1783
+ printJson(sdk, { ...result, cold_start: coldStart });
1725
1784
  } else {
1726
1785
  const rendered = renderHandoffNoteMarkdown(result.note) ?? result.note_raw;
1727
1786
  if (rendered) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "4.67.2",
3
+ "version": "4.68.0",
4
4
  "description": "CLI for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
5
5
  "type": "module",
6
6
  "bin": {