@dev.sail.money/sailor 1.1.0-61 → 1.1.0-62

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.sail.money/sailor",
3
- "version": "1.1.0-61",
3
+ "version": "1.1.0-62",
4
4
  "description": "Operator toolkit for Sail Protocol",
5
5
  "bin": {
6
6
  "sailor": "packages/cli/dist/index.cjs"
@@ -43604,6 +43604,7 @@ async function runCommand(opts) {
43604
43604
  for (const [k, v] of Object.entries(env)) {
43605
43605
  if (v && !process.env[k]) process.env[k] = v;
43606
43606
  }
43607
+ const runReason = opts.reason ?? process.env.SAIL_RUN_REASON ?? "manual";
43607
43608
  const configChainId = readJsonFile(sailPath("config.json"))?.chainId;
43608
43609
  const chainIdRaw = opts.chain != null ? String(opts.chain) : process.env.CHAIN_ID ?? env.CHAIN_ID ?? (configChainId != null ? String(configChainId) : void 0);
43609
43610
  if (!chainIdRaw) {
@@ -43733,7 +43734,7 @@ Configure the chain in the SDK chain registry or set KERNEL_ADDRESS in .sail/.en
43733
43734
  return d;
43734
43735
  };
43735
43736
  async function runTick() {
43736
- appendActivity({ ts: nowIso(), actor: "agent", type: "tick_start", chainId });
43737
+ appendActivity({ ts: nowIso(), actor: "agent", type: "tick_start", chainId, reason: runReason });
43737
43738
  let blockInfo = { number: 0n, timestamp: 0n };
43738
43739
  try {
43739
43740
  const block = await publicClient.getBlock();
@@ -43896,6 +43897,7 @@ Configure the chain in the SDK chain registry or set KERNEL_ADDRESS in .sail/.en
43896
43897
  console.log(`Account: ${accountAddr}`);
43897
43898
  console.log(`Chain: ${chainName2} (${chainId})`);
43898
43899
  console.log(once ? "Mode: single tick (--once)" : `Interval: ${intervalSec}s`);
43900
+ console.log(`Reason: ${runReason}`);
43899
43901
  console.log("Press Ctrl+C to stop");
43900
43902
  console.log("");
43901
43903
  writeAgentPid(chainId);
@@ -44042,6 +44044,110 @@ async function scan(options) {
44042
44044
  );
44043
44045
  }
44044
44046
 
44047
+ // src/commands/trigger.ts
44048
+ var import_node_child_process2 = require("node:child_process");
44049
+ var GH_API = "https://api.github.com";
44050
+ function parseRepoFromRemoteUrl(url) {
44051
+ const trimmed = url.trim().replace(/\.git$/, "");
44052
+ const m = trimmed.match(/github\.com[/:]([^/]+)\/([^/]+?)$/);
44053
+ return m ? `${m[1]}/${m[2]}` : null;
44054
+ }
44055
+ function resolveRepo(explicit) {
44056
+ if (explicit) {
44057
+ if (!/^[^/\s]+\/[^/\s]+$/.test(explicit)) {
44058
+ throw new Error(`--repo must be in "owner/repo" form \u2014 got: "${explicit}"`);
44059
+ }
44060
+ return explicit;
44061
+ }
44062
+ let url;
44063
+ try {
44064
+ url = (0, import_node_child_process2.execFileSync)("git", ["remote", "get-url", "origin"], {
44065
+ encoding: "utf-8",
44066
+ stdio: ["ignore", "pipe", "ignore"]
44067
+ }).trim();
44068
+ } catch {
44069
+ throw new Error(
44070
+ 'Could not read the git remote "origin".\nPass --repo <owner/repo> explicitly.'
44071
+ );
44072
+ }
44073
+ const repo = parseRepoFromRemoteUrl(url);
44074
+ if (!repo) {
44075
+ throw new Error(
44076
+ `Could not parse a GitHub owner/repo from the origin remote (${url}).
44077
+ Pass --repo <owner/repo>.`
44078
+ );
44079
+ }
44080
+ return repo;
44081
+ }
44082
+ function resolveToken() {
44083
+ const token = process.env.SAIL_GH_TOKEN ?? process.env.GITHUB_TOKEN;
44084
+ if (!token) {
44085
+ throw new Error(
44086
+ "No GitHub token found. Set SAIL_GH_TOKEN (or GITHUB_TOKEN) in your environment.\nIt needs the `actions: write` permission on the repo (a fine-grained PAT or a GitHub App token).\nThe token is read from the environment only \u2014 it is never passed as an argument or stored."
44087
+ );
44088
+ }
44089
+ return token;
44090
+ }
44091
+ function buildDispatchRequest(args) {
44092
+ return {
44093
+ url: `${GH_API}/repos/${args.repo}/actions/workflows/${encodeURIComponent(args.workflow)}/dispatches`,
44094
+ method: "POST",
44095
+ headers: {
44096
+ Authorization: `Bearer ${args.token}`,
44097
+ Accept: "application/vnd.github+json",
44098
+ "X-GitHub-Api-Version": "2022-11-28",
44099
+ "User-Agent": "sailor-cli",
44100
+ "Content-Type": "application/json"
44101
+ },
44102
+ body: JSON.stringify({ ref: args.ref, inputs: { reason: args.reason } })
44103
+ };
44104
+ }
44105
+ async function triggerGithub(options = {}) {
44106
+ const workflow = options.workflow ?? "agent-tick.yml";
44107
+ const ref = options.ref ?? "main";
44108
+ const reason = options.reason ?? "manual";
44109
+ const repo = resolveRepo(options.repo);
44110
+ const token = resolveToken();
44111
+ const req = buildDispatchRequest({ repo, workflow, ref, reason, token });
44112
+ let res;
44113
+ try {
44114
+ res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
44115
+ } catch (err) {
44116
+ throw new Error(`Could not reach GitHub to fire workflow_dispatch: ${err.message}`);
44117
+ }
44118
+ if (res.status === 204) {
44119
+ emit(
44120
+ options.json,
44121
+ () => {
44122
+ console.log(`\u2713 Triggered ${workflow} on ${repo}@${ref}`);
44123
+ console.log(` reason: ${reason}`);
44124
+ console.log(` runs: https://github.com/${repo}/actions/workflows/${workflow}`);
44125
+ },
44126
+ { status: "ok", repo, workflow, ref, reason }
44127
+ );
44128
+ return;
44129
+ }
44130
+ let detail = "";
44131
+ try {
44132
+ detail = await res.text();
44133
+ } catch {
44134
+ }
44135
+ let message = `GitHub returned ${res.status}`;
44136
+ if (res.status === 401) message += " \u2014 bad or expired token";
44137
+ else if (res.status === 403) message += " \u2014 token lacks `actions: write` on this repo";
44138
+ else if (res.status === 404)
44139
+ message += ` \u2014 workflow "${workflow}" or repo "${repo}" not found (or the token can't see it)`;
44140
+ if (detail) {
44141
+ try {
44142
+ const parsed = JSON.parse(detail);
44143
+ if (parsed.message) message += `: ${parsed.message}`;
44144
+ } catch {
44145
+ message += `: ${detail.slice(0, 200)}`;
44146
+ }
44147
+ }
44148
+ throw new Error(message);
44149
+ }
44150
+
44045
44151
  // src/commands/session.ts
44046
44152
  function requireAccount() {
44047
44153
  const account2 = readJsonFile(sailPath("account.json"));
@@ -44241,7 +44347,7 @@ async function status() {
44241
44347
  }
44242
44348
 
44243
44349
  // src/commands/ui.ts
44244
- var import_node_child_process2 = require("node:child_process");
44350
+ var import_node_child_process3 = require("node:child_process");
44245
44351
  var import_node_fs17 = __toESM(require("node:fs"), 1);
44246
44352
  var import_node_net2 = __toESM(require("node:net"), 1);
44247
44353
  var import_node_path13 = __toESM(require("node:path"), 1);
@@ -44311,7 +44417,7 @@ async function uiCommand() {
44311
44417
  import_node_fs17.default.mkdirSync(runtimeDir, { recursive: true });
44312
44418
  const logFile = import_node_path13.default.join(runtimeDir, "ui.log");
44313
44419
  const logFd = import_node_fs17.default.openSync(logFile, "a");
44314
- const child = (0, import_node_child_process2.spawn)(process.execPath, [serverBundle], {
44420
+ const child = (0, import_node_child_process3.spawn)(process.execPath, [serverBundle], {
44315
44421
  detached: true,
44316
44422
  stdio: ["ignore", logFd, logFd],
44317
44423
  env: { ...process.env, SAIL_DIR: sailDir2, SERVE_DIST: "1", PORT: String(port), SAILOR_UI_DIST: uiDistDir }
@@ -44462,9 +44568,16 @@ owner.command("connect").description("Open the signing station, wait for your wa
44462
44568
  owner.command("show").description("Show the saved project owner").option("--json", "Emit machine-readable JSON").action(actionWith(ownerShow));
44463
44569
  program2.command("scan").description("Discover the owner's SMAs, their permissions, and local keys; save to context.json").option("--owner <address>", "Owner address to scan (defaults to the saved project owner)").option("--json", "Emit machine-readable JSON").action(actionWith(scan));
44464
44570
  program2.command("status").description("Show current account, permission, and session status").action(action(status));
44465
- program2.command("run").description("Run the agent execution loop (use --once for a single tick)").option("--once", "Run a single tick then exit").option("--chain <chainId>", "Chain ID to run on (overrides CHAIN_ID env and .env.local)").action(async (opts) => {
44571
+ program2.command("run").description("Run the agent execution loop (use --once for a single tick)").option("--once", "Run a single tick then exit").option("--chain <chainId>", "Chain ID to run on (overrides CHAIN_ID env and .env.local)").option(
44572
+ "--reason <text>",
44573
+ "Label why this run fired (observability only; also read from SAIL_RUN_REASON)"
44574
+ ).action(async (opts) => {
44466
44575
  try {
44467
- await runCommand({ once: opts.once, chain: opts.chain ? Number(opts.chain) : void 0 });
44576
+ await runCommand({
44577
+ once: opts.once,
44578
+ chain: opts.chain ? Number(opts.chain) : void 0,
44579
+ reason: opts.reason
44580
+ });
44468
44581
  } catch (err) {
44469
44582
  console.error(`Error: ${err.message}`);
44470
44583
  closePrompts();
@@ -44472,6 +44585,8 @@ program2.command("run").description("Run the agent execution loop (use --once fo
44472
44585
  }
44473
44586
  closePrompts();
44474
44587
  });
44588
+ var trigger = program2.command("trigger").description("Wake the agent on demand from an external system");
44589
+ trigger.command("github").description("Fire the agent's GitHub Actions workflow_dispatch (the same job the cron runs)").option("--workflow <file>", "Workflow file to dispatch", "agent-tick.yml").option("--ref <branch>", "Git ref to run the workflow on", "main").option("--reason <text>", "Why this run fired \u2014 recorded as the workflow's reason input").option("--repo <owner/repo>", "Override the repository (default: from the git origin remote)").option("--json", "Emit machine-readable JSON").action(actionWith(triggerGithub));
44475
44590
  var session = program2.command("session").description("Control the agent session");
44476
44591
  session.command("pause").description("Pause the agent session (revoke dispatch rights)").action(action(sessionPause));
44477
44592
  session.command("resume").description("Resume a paused session").action(action(sessionResume));
@@ -5,7 +5,7 @@
5
5
  * Do not edit manually — run `pnpm build` to regenerate.
6
6
  *
7
7
  * Spec version : 1.2.0
8
- * Generated at : 2026-06-15T17:19:52.072Z
8
+ * Generated at : 2026-06-15T17:43:57.156Z
9
9
  */
10
10
  export declare const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
11
11
  export declare const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
@@ -5,7 +5,7 @@
5
5
  * Do not edit manually — run `pnpm build` to regenerate.
6
6
  *
7
7
  * Spec version : 1.2.0
8
- * Generated at : 2026-06-15T17:19:52.072Z
8
+ * Generated at : 2026-06-15T17:43:57.156Z
9
9
  */
10
10
  export const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
11
11
  export const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
@@ -1,10 +1,35 @@
1
1
  name: Agent Tick
2
2
 
3
+ # ─────────────────────────────────────────────────────────────────────────────
4
+ # WHEN does your agent run? Two mechanisms — you almost certainly need to tune
5
+ # the first one for your strategy.
6
+ #
7
+ # 1. schedule (cron, below) — a heartbeat / backstop. GitHub Actions cron is
8
+ # NON-PUNCTUAL: it drifts and is SKIPPED under load, so it is the wrong
9
+ # PRIMARY driver for anything latency-sensitive. Tune the cadence to your
10
+ # strategy's volatility:
11
+ # • LP / perps / liquidations → minutes — but don't rely on cron for it;
12
+ # use an external trigger (#2). Here cron is only a safety net.
13
+ # • DCA / periodic rebalance → daily.
14
+ # • treasury / slow rebalances → hourly to daily.
15
+ # The default below (hourly) is a GENERIC PLACEHOLDER — change it.
16
+ #
17
+ # 2. workflow_dispatch — an on-demand trigger. Anything that can make an HTTP
18
+ # call can wake this job the instant a condition is met (a price band, an
19
+ # on-chain event, a webhook). Fire it from the CLI: sailor trigger github
20
+ # This is how you get low-latency reaction that cron cannot give you.
21
+ # ─────────────────────────────────────────────────────────────────────────────
22
+
3
23
  on:
4
24
  schedule:
5
- # Every Monday at 09:00 UTC
6
- - cron: "0 9 * * 1"
25
+ # Generic placeholder hourly. TUNE THIS to your strategy (see above).
26
+ - cron: "0 * * * *"
7
27
  workflow_dispatch:
28
+ inputs:
29
+ reason:
30
+ description: "Why this run fired (recorded in the run log for auditability)"
31
+ required: false
32
+ default: "manual"
8
33
 
9
34
  jobs:
10
35
  tick:
@@ -30,4 +55,6 @@ jobs:
30
55
  RPC_URL: ${{ secrets.RPC_URL }}
31
56
  SAIL_PASSPHRASE: ${{ secrets.SAIL_PASSPHRASE }}
32
57
  CHAIN_ID: ${{ vars.CHAIN_ID || '8453' }}
58
+ # Labels the run: the dispatch reason, or "scheduled" for cron runs.
59
+ SAIL_RUN_REASON: ${{ github.event.inputs.reason || 'scheduled' }}
33
60
  run: npx sailor run --once