zas-agent 0.1.0 → 0.1.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.1] - 2026-09-02
11
+
12
+ ### Changed
13
+
14
+ - Every line the agent prints and every tool result is in English. The pairing
15
+ prompt, the status report and the error sentences were in Spanish.
16
+ - The README opens with what Zas is. The README, the package keywords and the
17
+ registry entry no longer call the design end-to-end encryption: the
18
+ product's own privacy page does not, because a separate service derives
19
+ account keys.
20
+
10
21
  ## [0.1.0] - 2026-09-02
11
22
 
12
23
  ### Added
@@ -25,5 +36,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
25
36
  two separate identities that cannot read each other's keys.
26
37
  - Install snippets for Claude Code and Codex, printed by `zas-agent pair`.
27
38
 
28
- [Unreleased]: https://github.com/soke1556/zas-agent/compare/v0.1.0...HEAD
39
+ [Unreleased]: https://github.com/soke1556/zas-agent/compare/v0.1.1...HEAD
40
+ [0.1.1]: https://github.com/soke1556/zas-agent/compare/v0.1.0...v0.1.1
29
41
  [0.1.0]: https://github.com/soke1556/zas-agent/releases/tag/v0.1.0
package/README.md CHANGED
@@ -10,6 +10,19 @@ read items back out of them.
10
10
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/soke1556/zas-agent/badge)](https://scorecard.dev/viewer/?uri=github.com/soke1556/zas-agent)
11
11
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
12
12
 
13
+ ## What is Zas
14
+
15
+ [Zas](https://zas.red) moves things between your devices and the people you
16
+ choose: files, photos, text, code. You put something into a channel on one
17
+ device and use it on another. Whatever you do not pin leaves on its own after a
18
+ few days, so there is nothing to tidy up later. Everything is encrypted on the
19
+ device before it is uploaded: the database and the object storage receive
20
+ encrypted bytes, not filenames, content or previews.
21
+
22
+ This package is the piece that lets a coding agent use your channels the way
23
+ your other devices do, under an identity of its own that you approve, that you
24
+ scope to the channels you choose, and that you can revoke.
25
+
13
26
  ## What it does
14
27
 
15
28
  Your coding agent gets seven tools. It can send a file or a note into a channel
@@ -56,11 +69,11 @@ args = ["-y", "zas-agent", "--profile", "codex"]
56
69
  terminal shows four things:
57
70
 
58
71
  ```
59
- Abrí esta página con tu cuenta de Zas:
72
+ Open this page signed in to your Zas account:
60
73
  https://zas.red/agents/pair?p=...
61
- Código: ABCD-EFGH
62
- Huella: 1a2b 3c4d 5e6f 7a8b
63
- Esperando la aprobación… (vence en 10 minutos)
74
+ Code: ABCD-EFGH
75
+ Fingerprint: 1a2b 3c4d 5e6f 7a8b
76
+ Waiting for approval… (expires in 10 minutes)
64
77
  ```
65
78
 
66
79
  Open the link signed in to your Zas account. Check that the fingerprint on the
@@ -98,11 +111,12 @@ source, the sentence says so.
98
111
  - **Everything it sends is visible as its work.** Every item carries the `>_`
99
112
  agent mark and the agent's name, in the channel, on every device you read Zas
100
113
  from.
101
- - **You can revoke it at any time.** Settings → Agentes → the agent → Revocar.
114
+ - **You can revoke it at any time.** Settings → Agents → the agent → Revoke.
102
115
  The session stops, its refresh tokens are revoked *(server-side)*, and the
103
116
  next tool call answers "the owner revoked this agent". What it already sent
104
117
  stays where it is. You can also drop a single channel and keep the rest.
105
- - **Content is end-to-end encrypted, the same way the app does it.** The
118
+ - **Content is encrypted on your machine before it leaves, the same way the
119
+ app does it.** The
106
120
  chunking, the manifest and the envelope formats under `src/shared/` are the
107
121
  same modules the Zas web app ships. The server stores ciphertext and never
108
122
  sees a channel key, a channel name or item plaintext *(server-side)*.
@@ -131,7 +145,7 @@ ones marked *(server-side)* are enforced by the Zas server.
131
145
  - **Rate limited by the server**, on its own buckets, with the key-derivation
132
146
  budget charged to your account so ten agents are not ten times your own
133
147
  allowance *(server-side)*.
134
- - **Ten agents per account** *(server-side)*.
148
+ - **As many agents as your plan or your organization allows** *(server-side)*.
135
149
  - **Files up to 5 GiB**, and in practice less: the agent reads a file into
136
150
  memory to hash it, so the machine's memory is the real ceiling.
137
151
 
@@ -171,7 +185,7 @@ string ever reaches a terminal.
171
185
  | `missing_token` | The session token is missing; pair the agent again. |
172
186
  | `internal` | Something failed inside the agent. |
173
187
 
174
- Every code comes back as one sentence in Spanish and one in English, never as a
188
+ Every code comes back as one sentence in English, never as a
175
189
  stack trace.
176
190
 
177
191
  ## Tools
@@ -233,7 +247,7 @@ profile they live in, and the package does not try to set any others.
233
247
 
234
248
  Deleting the directory makes this machine forget the agent. It does not revoke
235
249
  anything: the account side is closed from the web app, under
236
- Settings → AgentesRevocar.
250
+ Settings → AgentsRevoke.
237
251
 
238
252
  ## Configuration
239
253
 
package/dist/cli.js CHANGED
@@ -136,38 +136,38 @@ function errorFromResponse(status, body) {
136
136
  return new ZasError(code, status, raw, retryAfterMs, raw);
137
137
  }
138
138
  var SENTENCES = {
139
- identity_corrupt: { es: "El archivo de identidad del agente est\xE1 da\xF1ado: {path}. Restauralo o borralo y volv\xE9 a emparejar.", en: "The agent identity file is damaged: {path}. Restore it, or delete it and pair again." },
140
- not_paired: { es: "Este agente todav\xEDa no est\xE1 emparejado. Corr\xE9 \xABnpx -y zas-agent pair\xBB en la terminal.", en: "This agent is not paired yet. Run \u201Cnpx -y zas-agent pair\u201D in the terminal." },
141
- internal: { es: "Algo fall\xF3 dentro del agente. Prob\xE1 de nuevo.", en: "Something failed inside the agent. Try again." },
142
- agent_forbidden: { es: "Este agente no puede hacer eso; solo el due\xF1o de la cuenta.", en: "This agent cannot do that; only the account owner can." },
143
- agent_revoked: { es: "El due\xF1o revoc\xF3 este agente. Volv\xE9 a emparejar con \xABzas-agent pair\xBB.", en: "The owner revoked this agent. Pair again with \u201Czas-agent pair\u201D." },
144
- grant_missing: { es: "Este agente no tiene acceso a ese canal. El due\xF1o lo agrega desde Ajustes \u2192 Agentes.", en: "This agent has no access to that channel. The owner adds it from Settings \u2192 Agents." },
145
- not_found: { es: "Ese \xEDtem no est\xE1 en el canal.", en: "That item is not in the channel." },
146
- invalid_cap: { es: "Ese archivo ya no est\xE1 disponible.", en: "That file is no longer available." },
147
- write_failed: { es: "No se pudo guardar el archivo en el destino.", en: "The file could not be saved to the destination." },
148
- send_forbidden: { es: "Este agente no puede enviar a ese canal.", en: "This agent cannot send to that channel." },
149
- read_forbidden: { es: "Este agente no puede leer ese canal.", en: "This agent cannot read that channel." },
150
- direct_mode: { es: "Ese canal est\xE1 en modo Directo. Us\xE1 zas_send_direct.", en: "That channel is in Directo mode. Use zas_send_direct." },
151
- not_direct_mode: { es: "Ese canal no est\xE1 en modo Directo. Us\xE1 zas_send_file.", en: "That channel is not in Directo mode. Use zas_send_file." },
152
- key_stale: { es: "La clave del canal cambi\xF3. El due\xF1o la renueva al abrir Zas.", en: "The channel key changed. The owner refreshes it by opening Zas." },
153
- quota_exceeded: { es: "La cuenta lleg\xF3 a su l\xEDmite de almacenamiento.", en: "The account reached its storage limit." },
154
- rate_limited: { es: "Demasiados env\xEDos seguidos. Esper\xE1 un momento.", en: "Too many sends in a row. Wait a moment." },
155
- file_too_big: { es: "El archivo supera el m\xE1ximo del plan.", en: "The file is over the plan limit." },
156
- duplicate: { es: "Ese \xEDtem ya est\xE1 en el canal.", en: "That item is already in the channel." },
157
- pairing_expired: { es: "El emparejamiento venci\xF3. Corr\xE9 \xABzas-agent pair\xBB de nuevo.", en: "The pairing expired. Run \u201Czas-agent pair\u201D again." },
158
- pairing_cancelled: { es: "El due\xF1o cancel\xF3 el emparejamiento.", en: "The owner cancelled the pairing." },
159
- feature_disabled: { es: "Los agentes todav\xEDa no est\xE1n habilitados para esta cuenta.", en: "Agents are not enabled for this account yet." },
160
- upload_failed: { es: "No se pudo subir el archivo. Prob\xE1 de nuevo.", en: "The upload failed. Try again." },
161
- oprf_failed: { es: "Zas no respondi\xF3 bien al preparar el archivo. Prob\xE1 de nuevo.", en: "Zas did not answer correctly while preparing the file. Try again." },
162
- network: { es: "No hay conexi\xF3n con Zas. Prob\xE1 de nuevo en un momento.", en: "Zas cannot be reached. Try again in a moment." },
163
- sign_in_failed: { es: "Zas no acept\xF3 la sesi\xF3n de este agente. Prob\xE1 de nuevo o volv\xE9 a emparejar.", en: "Zas did not accept this agent session. Try again, or pair again." },
164
- bad_signature: { es: "Zas rechaz\xF3 la firma de este agente. Emparejalo de nuevo.", en: "Zas rejected this agent's signature. Pair it again." },
165
- missing_token: { es: "Falta el token de sesi\xF3n. Emparej\xE1 el agente de nuevo.", en: "The session token is missing. Pair the agent again." }
139
+ identity_corrupt: "The agent identity file is damaged: {path}. Restore it, or delete it and pair again.",
140
+ not_paired: "This agent is not paired yet. Run \u201Cnpx -y zas-agent pair\u201D in the terminal.",
141
+ internal: "Something failed inside the agent. Try again.",
142
+ agent_forbidden: "This agent cannot do that; only the account owner can.",
143
+ agent_revoked: "The owner revoked this agent. Pair again with \u201Czas-agent pair\u201D.",
144
+ grant_missing: "This agent has no access to that channel. The owner adds it from Settings \u2192 Agents.",
145
+ not_found: "That item is not in the channel.",
146
+ invalid_cap: "That file is no longer available.",
147
+ write_failed: "The file could not be saved to the destination.",
148
+ send_forbidden: "This agent cannot send to that channel.",
149
+ read_forbidden: "This agent cannot read that channel.",
150
+ direct_mode: "That channel is in Directo mode. Use zas_send_direct.",
151
+ not_direct_mode: "That channel is not in Directo mode. Use zas_send_file.",
152
+ key_stale: "The channel key changed. The owner refreshes it by opening Zas.",
153
+ quota_exceeded: "The account reached its storage limit.",
154
+ rate_limited: "Too many sends in a row. Wait a moment.",
155
+ file_too_big: "The file is over the plan limit.",
156
+ duplicate: "That item is already in the channel.",
157
+ pairing_expired: "The pairing expired. Run \u201Czas-agent pair\u201D again.",
158
+ pairing_cancelled: "The owner cancelled the pairing.",
159
+ feature_disabled: "Agents are not enabled for this account yet.",
160
+ upload_failed: "The upload failed. Try again.",
161
+ oprf_failed: "Zas did not answer correctly while preparing the file. Try again.",
162
+ network: "Zas cannot be reached. Try again in a moment.",
163
+ sign_in_failed: "Zas did not accept this agent session. Try again, or pair again.",
164
+ bad_signature: "Zas rejected this agent's signature. Pair it again.",
165
+ missing_token: "The session token is missing. Pair the agent again."
166
166
  };
167
- function humanSentence(error, locale = "es") {
168
- const sentence = SENTENCES[error.code]?.[locale];
167
+ function humanSentence(error) {
168
+ const sentence = SENTENCES[error.code];
169
169
  if (sentence) return sentence.replace("{path}", () => error.message);
170
- return locale === "es" ? `Zas respondi\xF3 ${error.code} (${error.status}).` : `Zas answered ${error.code} (${error.status}).`;
170
+ return `Zas answered ${error.code} (${error.status}).`;
171
171
  }
172
172
 
173
173
  // src/identity.ts
@@ -185,7 +185,7 @@ function agentHome() {
185
185
  }
186
186
  var PROFILE_RE = /^(?!\.)[A-Za-z0-9._-]{1,64}$/;
187
187
  function profileDir(profile) {
188
- if (!PROFILE_RE.test(profile)) throw new ZasError("internal", 0, `Perfil inv\xE1lido: ${profile}`);
188
+ if (!PROFILE_RE.test(profile)) throw new ZasError("internal", 0, `Invalid profile: ${profile}`);
189
189
  return join(agentHome(), profile);
190
190
  }
191
191
  function ensureDir(dir) {
@@ -453,7 +453,7 @@ async function runPair(opts) {
453
453
  const existing = loadIdentity(opts.profile);
454
454
  if (existing) {
455
455
  opts.log(
456
- `Este perfil ya est\xE1 emparejado como \xAB${existing.name}\xBB. Se va a crear un agente nuevo; revoc\xE1 el anterior desde Ajustes \u2192 Agentes.`
456
+ `This profile is already paired as \u201C${existing.name}\u201D. A new agent will be created; revoke the old one from Settings \u2192 Agents.`
457
457
  );
458
458
  }
459
459
  const host = (opts.host ?? hostname()).slice(0, AGENT_HOST_MAX);
@@ -485,11 +485,11 @@ async function runPair(opts) {
485
485
  const minutesLeft = Math.max(0, Math.round((created.expires_at - now()) / 6e4));
486
486
  opts.log(
487
487
  [
488
- "Abr\xED esta p\xE1gina con tu cuenta de Zas:",
488
+ "Open this page signed in to your Zas account:",
489
489
  ` ${pairUrl(opts.webBase, created.pairing_id)}`,
490
- `C\xF3digo: ${formatCode(created.code)}`,
491
- `Huella: ${agentFingerprintShort(created.fingerprint)}`,
492
- `Esperando la aprobaci\xF3n\u2026 (vence en ${minutesLeft} minutos)`
490
+ `Code: ${formatCode(created.code)}`,
491
+ `Fingerprint: ${agentFingerprintShort(created.fingerprint)}`,
492
+ `Waiting for approval\u2026 (expires in ${minutesLeft} minutes)`
493
493
  ].join("\n")
494
494
  );
495
495
  let approved = null;
@@ -538,10 +538,10 @@ async function runPair(opts) {
538
538
  clearPending(opts.profile);
539
539
  const claudeLines = (lead) => [lead, ` ${claudeSnippet(opts.profile)}`];
540
540
  const codexLines = (lead) => [lead, ` ${codexSnippet(opts.profile)}`];
541
- const install = opts.kind === "claude_code" ? claudeLines("Agregalo a Claude Code:") : opts.kind === "codex" ? codexLines("Agregalo a Codex:") : [...claudeLines("Agregalo a Claude Code:"), ...codexLines("O a Codex:")];
541
+ const install = opts.kind === "claude_code" ? claudeLines("Add it to Claude Code:") : opts.kind === "codex" ? codexLines("Add it to Codex:") : [...claudeLines("Add it to Claude Code:"), ...codexLines("Or to Codex:")];
542
542
  opts.log(
543
543
  [
544
- `Listo: el agente \xAB${identity.name}\xBB qued\xF3 emparejado con tu cuenta.`,
544
+ `Done: the agent \u201C${identity.name}\u201D is paired with your account.`,
545
545
  ...install
546
546
  ].join("\n")
547
547
  );
@@ -1631,7 +1631,7 @@ async function sendNote(ctx, input) {
1631
1631
 
1632
1632
  // src/server.ts
1633
1633
  function agentVersion() {
1634
- return true ? "0.1.0" : "0.0.0-dev";
1634
+ return true ? "0.1.1" : "0.0.0-dev";
1635
1635
  }
1636
1636
  var AGENT_CUE = " The owner sees every item this agent sends with the >_ agent mark and this agent's name, on every device.";
1637
1637
  var PAIR_ANNOUNCE_MS = 15e3;
@@ -1641,9 +1641,9 @@ var delay = (ms) => new Promise((resolve2) => {
1641
1641
  });
1642
1642
  function rightsOf(grant) {
1643
1643
  const rights = [];
1644
- if (grant.send && grant.mode !== "view" && !grant.direct_mode) rights.push("env\xEDa");
1645
- if (grant.read) rights.push("lee");
1646
- return rights.length > 0 ? rights.join(" \xB7 ") : "sin permisos";
1644
+ if (grant.send && grant.mode !== "view" && !grant.direct_mode) rights.push("send");
1645
+ if (grant.read) rights.push("read");
1646
+ return rights.length > 0 ? rights.join(" \xB7 ") : "no access";
1647
1647
  }
1648
1648
  function buildServer(profile, deps = {}) {
1649
1649
  const server = new McpServer({ name: "zas", version: agentVersion() });
@@ -1664,7 +1664,7 @@ function buildServer(profile, deps = {}) {
1664
1664
  });
1665
1665
  const failed = (e) => {
1666
1666
  const err = e instanceof ZasError ? e : new ZasError("internal", 0, String(e));
1667
- return { isError: true, ...text2(`${err.code}: ${humanSentence(err, "es")} / ${humanSentence(err, "en")}`) };
1667
+ return { isError: true, ...text2(`${err.code}: ${humanSentence(err)}`) };
1668
1668
  };
1669
1669
  const settled = (job) => {
1670
1670
  if (job.status === "done") return text2(job.result);
@@ -1679,7 +1679,7 @@ function buildServer(profile, deps = {}) {
1679
1679
  }, async () => {
1680
1680
  try {
1681
1681
  if (!(deps.identity ?? loadIdentity(profile))) {
1682
- return text2(humanSentence(new ZasError("not_paired", 0), "es"));
1682
+ return text2(humanSentence(new ZasError("not_paired", 0)));
1683
1683
  }
1684
1684
  const c = ctx();
1685
1685
  const grants = await grantsFor(c.client, profile);
@@ -1692,10 +1692,10 @@ function buildServer(profile, deps = {}) {
1692
1692
  return ` ${name} \xB7 ${rightsOf(grant)}`;
1693
1693
  });
1694
1694
  return text2([
1695
- `Emparejado como \xAB${c.identity.name}\xBB (${c.identity.kind}) con la cuenta ${c.identity.owner_uid}.`,
1696
- grants.length > 0 ? "Canales:" : "Sin canales: el due\xF1o todav\xEDa no le dio acceso a ninguno.",
1695
+ `Paired as \u201C${c.identity.name}\u201D (${c.identity.kind}) with account ${c.identity.owner_uid}.`,
1696
+ grants.length > 0 ? "Channels:" : "No channels: the owner has not granted access to any yet.",
1697
1697
  ...lines,
1698
- `${packageName()} ${agentVersion()} \xB7 perfil ${profile}`
1698
+ `${packageName()} ${agentVersion()} \xB7 profile ${profile}`
1699
1699
  ].join("\n"));
1700
1700
  } catch (e) {
1701
1701
  return failed(e);
@@ -1736,7 +1736,7 @@ function buildServer(profile, deps = {}) {
1736
1736
  pairing = null;
1737
1737
  return failed(state.error);
1738
1738
  }
1739
- return text2(state.logs.join("\n") || "Abriendo el emparejamiento\u2026");
1739
+ return text2(state.logs.join("\n") || "Opening the pairing\u2026");
1740
1740
  }
1741
1741
  if (pairing.status === "paired") return text2(`paired as ${pairing.identity?.name ?? ""}`.trim());
1742
1742
  if (pairing.status === "failed") {
@@ -1840,7 +1840,7 @@ function parseArgs(argv) {
1840
1840
  else if (name === "--profile") {
1841
1841
  parsed.profile = take() || DEFAULT_PROFILE;
1842
1842
  if (!PROFILE_RE.test(parsed.profile)) {
1843
- return { ...parsed, command: "invalid", message: `Perfil inv\xE1lido: ${parsed.profile}` };
1843
+ return { ...parsed, command: "invalid", message: `Invalid profile: ${parsed.profile}` };
1844
1844
  }
1845
1845
  } else if (name === "--kind") {
1846
1846
  const kind = take();
@@ -1870,7 +1870,7 @@ async function main(argv, log = (l) => console.error(l)) {
1870
1870
  return 2;
1871
1871
  }
1872
1872
  if (args.command === "help") {
1873
- if (args.unknown) log(`No entiendo \xAB${args.unknown}\xBB.`);
1873
+ if (args.unknown) log(`Unknown argument \u201C${args.unknown}\u201D.`);
1874
1874
  log(USAGE);
1875
1875
  return args.unknown ? 2 : 0;
1876
1876
  }
@@ -1886,7 +1886,7 @@ async function main(argv, log = (l) => console.error(l)) {
1886
1886
  });
1887
1887
  } catch (e) {
1888
1888
  const err = e instanceof ZasError ? e : new ZasError("internal", 0, String(e));
1889
- log(humanSentence(err, "es"));
1889
+ log(humanSentence(err));
1890
1890
  return 1;
1891
1891
  }
1892
1892
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zas-agent",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "MCP server that lets a coding agent send files and notes into your Zas channels.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "claude-code",
19
19
  "codex",
20
20
  "zas",
21
- "end-to-end-encryption"
21
+ "encryption"
22
22
  ],
23
23
  "mcpName": "io.github.soke1556/zas-agent",
24
24
  "bin": {