@c4roa0/agentpay-user 0.3.2 → 0.3.4

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
@@ -33,6 +33,24 @@ normally live in `~/.local/share/agentpay/user`; an existing custom data directo
33
33
  is retained. Installation checkpoints use `~/.local/state/agentpay/user` (or
34
34
  `$XDG_STATE_HOME/agentpay/user`). Upgrading program files does not erase either.
35
35
 
36
+ ## Install in a folder instead
37
+
38
+ For a directory-owned installation, choose a program folder:
39
+
40
+ ```sh
41
+ mkdir agentpay-local
42
+ cd agentpay-local
43
+ npm install @c4roa0/agentpay-user
44
+ ./node_modules/.bin/agentpay help
45
+ ```
46
+
47
+ Use `./node_modules/.bin/agentpay` instead of `agentpay` in the commands below.
48
+ `update` updates this installation and its project lockfile; it does not install
49
+ User globally. For an existing Wallet, use `repair` to refresh the background
50
+ services against this installation. Private data stays in its existing directory.
51
+ Before deleting the program folder, run `uninstall` to remove managed services;
52
+ private data is kept unless you explicitly choose `--delete-data`.
53
+
36
54
  ## After setup
37
55
 
38
56
  ```sh
@@ -44,7 +62,7 @@ agentpay market connect
44
62
 
45
63
  Linux background services use your systemd user session. Wallet starts locked after
46
64
  a restart. `agentpay service help` explains startup, stop and logs; `agentpay help` lists
47
- all groups. Global npm installations use `agentpay update` to review and install a
65
+ all groups. npm installations use `agentpay update` to review and install a
48
66
  new User version with its matching Wallet and official test Skill. Source checkouts
49
67
  update their files, run `npm ci`, then `agentpay repair`. [Commands](doc/runbooks/commands.md) · [Updates](doc/runbooks/local-update.md)
50
68
  · [Backup and restore](doc/runbooks/wallet-identity.md).
package/doc/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # User documentation
2
2
 
3
+ - [Directory npm update verification](records/local-npm-update-2026-11-08.md): real package update and preflight boundary.
4
+
3
5
  ## Start by role
4
6
 
5
7
  | Reader | Start here |
package/doc/TODO.md CHANGED
@@ -1,10 +1,25 @@
1
1
  # User TODO
2
2
 
3
+ ## Existing Market address update
4
+
5
+ - [x] Reauthenticate and verify original account, Wallet tuple and Policy connection before replacing an address.
6
+ - [x] Verify unchanged deployment; preserve RPC, key, Policy, history/cache and original cancellation IDs.
7
+ - [x] Apply under installer lock, restart managed Runtime, retain retry marker and show incomplete update in status.
8
+ - [x] Full regression (387 checks), followed by targeted reconnect-boundary/transaction checks.
9
+ - [ ] Publish a reviewed version and verify the existing participant installation against HTTPS.
10
+
11
+ ## Directory npm update, then HTTPS
12
+
13
+ - [x] Support directory-local npm update without replacing source/link installs.
14
+ - [x] Verify real npm directory upgrade and isolated preflight; nine regression checks passed.
15
+ - [x] Publish 0.3.3 via v0.3.3 and independently verify registry archive/dependency pins.
16
+ - [ ] Verify participant directory install/update.
17
+
3
18
  ## Tag-triggered publication
4
19
 
5
20
  - [x] Use matching `vX.Y.Z` tags for npm publication; retain verification gates.
6
21
  - [x] Owner confirmed release tags allowed in the GitHub `npm-publish` environment.
7
- - [ ] Verify the first tag-triggered OIDC publication and registry integrity.
22
+ - [x] Verify first tag-triggered OIDC run 42491753287 and registry archive integrity.
8
23
 
9
24
  ## Official package 1.2.1 — 2026-11-08
10
25
 
@@ -13,7 +28,8 @@
13
28
  - [x] Load the reviewed User source update and run the normal 1.2.1 payment test with explicit user consent; terminal delivery/result/ack completed.
14
29
  - [x] Verify website visibility and independently reconcile Settlement/chain outcome; prepare User 0.3.2 candidate.
15
30
  - [x] Pin the verified public Wallet 0.3.2; align package/lockfile/update-pair.
16
- - [ ] Publish User 0.3.2 through OIDC and verify registry artifact; then accept a real npm update.
31
+ - [x] Publish User 0.3.2 through tag-triggered OIDC; verify public registry archive.
32
+ - [ ] Accept a real participant npm update/restart.
17
33
 
18
34
 
19
35
  ## Fixed Skill download loop — 2026-11-08
@@ -30,6 +30,7 @@ name change. Independent-version compatibility remains a separate planned featur
30
30
  | Area | Main files | Responsibility |
31
31
  | --- | --- | --- |
32
32
  | CLI | `run`, `scripts/agentpay-cli.mjs`, `src/user-cli.mjs` | Node discovery, grouped commands and help |
33
+ | npm update | `npm-update.mjs` | Detect global/direct-project installation, isolate npm preflight, pin the reviewed release and repair services without changing private data |
33
34
  | Install / repair | `install-user.mjs`, `install-and-setup.mjs`, `install-preflight.mjs`, `local-update.mjs` | Dependency/build preparation, checkpoints, downloaded settings and safe restart |
34
35
  | Setup | `guided-setup.mjs`, `user-setup-guide.mjs`, `anvil-onboarding.mjs` | Terminal sequence and saved-step continuation; explicit signer mode |
35
36
  | Permission | `policy-terminal-guide.mjs`, `safe-policy-plan.mjs`, `renew-policy.mjs`, `policy-operations.mjs`, `policy-migration.mjs` | Drafts, approved operations and verified local binding |
@@ -97,3 +98,20 @@ required for the local test suite.
97
98
  Tests use public interfaces and controlled remote responses. The explicit fault
98
99
  loader only instruments a spawned test Runtime; production startup contains no
99
100
  crash-test switch. See [test guide](https://github.com/c4rO-0/agentpay-user/blob/main/test/README.md) for evidence limits.
101
+
102
+ ## Explicit platform address changes
103
+
104
+ `connect-market.mjs` reauthenticates at the user-selected origin and checks the
105
+ original account, Wallet tuple and exact Policy connection ID. Public configuration
106
+ is not a Market identity proof. `platform-endpoints.mjs` compares saved deployment
107
+ and bound Policy with the new configuration, probes Settlement's existing public
108
+ readiness projection, then changes only transport URLs after consent. No new API
109
+ or platform identity key is introduced.
110
+
111
+ The shared installer lock serializes the local apply step. Only the managed Runtime
112
+ unit is stopped; its listener must be gone before writes. Private atomic files and
113
+ a pending marker allow retry after interruption. Wallet and Order files are never
114
+ rewritten. `settlement-address-history.mjs` preserves exact directed, Policy-scoped
115
+ address mappings so cancellation retries retain their immutable original IDs.
116
+ Runtime recovery observations remain tagged by source and are rechecked instead of
117
+ being relabelled. The pending marker is surfaced by status.
@@ -0,0 +1,32 @@
1
+ # Directory npm update verification — 2026-11-08
2
+
3
+ The updater accepts a direct registry dependency in a project installation as well
4
+ as a global installation. The owning folder comes from the running package, not
5
+ the terminal working directory. Source/link, custom-source and transitive installs
6
+ remain outside automatic updates. Confirmation, exact release pinning, private-data
7
+ retention and the existing service repair flow remain in effect.
8
+
9
+ Nine controlled regression checks passed, covering global/local update, cancellation,
10
+ forced confirmation, failures, concurrent changes and unsupported install layouts.
11
+ A real-npm check installed public User 0.3.1 into a temporary project, ran the changed
12
+ updater against public 0.3.2 and verified program, manifest and lockfile versions.
13
+ Private-record fixture bytes were preserved. Service stop/repair hooks were controlled;
14
+ this is not participant process recovery or paid acceptance.
15
+
16
+ npm 10.9.8 dry-run changed node_modules/.package-lock.json without replacing program
17
+ files. The subsequent install could report up to date while leaving old code.
18
+ Lock-related flags did not reliably prevent this. Preflight now uses a disposable
19
+ prefix with privately copied project npm settings for consistent registry selection,
20
+ removed afterward. The real check confirms the existing hidden lockfile stays on
21
+ the old version after preflight. No participant keys, services or payments were used.
22
+
23
+ Follow-up publication and participant directory-update acceptance remain pending.
24
+
25
+ User 0.3.3 release preparation (2026-11-09): all 383 behavior checks and standalone
26
+ archive installation with isolated Wallet/Runtime process checks passed. Wallet
27
+ 0.3.2, Protocol 0.14.3 and official package pin are unchanged. Publication and
28
+ participant acceptance remain separate gates.
29
+
30
+ Publication verified on 2026-11-09: tag v0.3.3 at c5f3be1, GitHub run
31
+ 42567636609. Public npm archive matches the final local release archive byte for
32
+ byte; provenance metadata is present. Participant acceptance remains pending.
@@ -2,7 +2,8 @@
2
2
 
3
3
  Use `agentpay` after installing the package or linking a source checkout with
4
4
  `npm link`. From a source folder, `./run` is the same entry; it uses Node on PATH
5
- or `AGENTPAY_NODE`.
5
+ or `AGENTPAY_NODE`. For a folder-local npm install, use
6
+ `./node_modules/.bin/agentpay`; its `update` stays in that installation folder.
6
7
 
7
8
  ```sh
8
9
  agentpay help
@@ -8,18 +8,35 @@ For a global npm installation:
8
8
  agentpay update
9
9
  ```
10
10
 
11
+ For an npm installation inside a project folder, run its own command:
12
+
13
+ ```sh
14
+ ./node_modules/.bin/agentpay update
15
+ ```
16
+
17
+ Directory-local update support starts with User 0.3.3. For an older directory
18
+ installation, run `npm install --save-exact @c4roa0/agentpay-user@0.3.3` in its
19
+ original program folder, then `./node_modules/.bin/agentpay repair`. Future versions
20
+ can be installed with the command above. Keep your private data directory.
21
+
22
+ Both forms update the installation that owns the command, regardless of the
23
+ terminal's working directory. Directory installs update their direct User dependency
24
+ and package lockfile in that folder, with an exact version; global installs keep
25
+ their global prefix. The confirmation shows the target folder. User must be a direct
26
+ registry dependency, not a transitive dependency or a linked/custom-source package.
27
+
11
28
  It checks the published User version and shows its exact Wallet dependency. Confirm
12
29
  to continue, or cancel without changing services. `agentpay update -f` skips only
13
- this confirmation. The updater checks requirements, stops configured services,
30
+ this confirmation. The updater checks requirements in a temporary installation directory, stops configured services,
14
31
  installs the confirmed version and invokes the new version's repair to refresh
15
32
  services and verify its pinned official test Skill. No paid test runs automatically.
16
33
  Other installed Skills and original requests are kept. Wallet needs unlocking after
17
34
  restart. A current User version still checks the official test Skill.
18
35
 
19
- Only genuine global npm installations are updated. A source checkout or `npm link`
36
+ Only genuine npm installations are updated. A source checkout or `npm link`
20
37
  is refused rather than overwritten: update User's files, run `npm ci`, then
21
- `agentpay repair`. The first User 0.3.0 release establishes the package baseline; a later real
22
- registry upgrade still needs participant acceptance. The updater uses npm's configured registry, checks
38
+ `agentpay repair`. Directory update has controlled real-npm verification; participant upgrade acceptance
39
+ remains separate. The updater uses npm's configured registry, checks
23
40
  Node engine requirements and disables package installation hooks; releases must
24
41
  ship usable runtime artifacts. It never independently selects the newest Wallet.
25
42
 
@@ -16,6 +16,39 @@ Review the account and nickname, then type `confirm`. Use `details` to inspect t
16
16
  Safe, network and Policy. Open Market → Account to view your Wallets and Orders.
17
17
  A separate browser login may be needed; terminal sign-in does not create browser cookies.
18
18
 
19
+ ## Change the address of the same Market
20
+
21
+ In a version containing the address-update feature, run:
22
+
23
+ ```sh
24
+ agentpay market connect --market https://YOUR_NEW_MARKET_ADDRESS
25
+ ```
26
+
27
+ For a folder installation, use `./node_modules/.bin/agentpay` instead of `agentpay`.
28
+ Sign in again at the address you trust. User checks that the account ID and the
29
+ original Wallet/Policy connection are still present. The existing nickname is
30
+ reused. It also checks the network, contracts and Settlement deployment before
31
+ showing the new platform addresses for confirmation.
32
+
33
+ Confirm to update the saved Market address and, if changed, the Settlement and
34
+ package download addresses. Runtime pauses briefly and restarts; Wallet remains
35
+ unlocked. Keys, Policy, Orders, input/output caches, startup preference and the
36
+ participant's RPC address stay unchanged. No paid test runs automatically.
37
+
38
+ A different account, missing original connection or changed contracts stops the
39
+ address update. This is a deliberate address change, not automatic discovery or
40
+ cryptographic proof of server identity. Check the website address yourself; the
41
+ terminal never forwards a previously saved login token to the new address.
42
+
43
+ If interrupted, repeat the same command with the same address. `status` reports
44
+ an incomplete update until it finishes. If a killed process leaves an installation
45
+ lock, confirm no installer is running before following the lock-review guidance;
46
+ User does not break an unknown live lock automatically. Existing cancellation requests keep their
47
+ original IDs; recovery checks refresh observations from the new Settlement.
48
+ Keep any old SSH tunnels until the update succeeds and `status` confirms operation.
49
+ This feature requires User 0.3.4 or newer. Updating software does not change
50
+ your saved addresses; run the command above to review the change.
51
+
19
52
  ## Wallet identity and Policy access
20
53
 
21
54
  A Wallet is identified by network + Module + Safe + execution address. Its nickname
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4roa0/agentpay-user",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "@c4roa0/agentpay-protocol": "0.14.3",
package/progress.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # AgentPay User progress
2
2
 
3
+ ## User 0.3.4 release candidate — 2026-11-09
4
+
5
+ Release the explicit Market address update with unchanged Wallet 0.3.2 and
6
+ Protocol 0.14.3 pins. No participant data or running services are changed by
7
+ publication. Independent npm archive installation and real isolated local-service
8
+ startup passed; all 388 release regressions passed. Tag publication, registry verification and participant migration
9
+ are separate gates; participant acceptance remains pending.
10
+
11
+
12
+ ## Explicit Market address update — 2026-11-09
13
+
14
+ Approved reduced scope: reuse login, Wallet list and public deployment interfaces;
15
+ no Market identity key or new backend endpoint. User-selected address changes must
16
+ match the original account/Wallet/Policy connection and deployment. Consent updates
17
+ transport URLs, restarts only managed Runtime and preserves key/Policy/Orders/cache.
18
+ Policy-scoped aliases retain cancellation IDs; interrupted apply has a retry marker
19
+ and status guidance. Controlled address/connection/cancellation/status checks pass;
20
+ 387 full regression checks passed; subsequent reconnect-boundary/transaction
21
+ changes passed targeted checks. npm publication and participant acceptance remain
22
+ pending. Only explicit origin changes or interrupted updates enter this path;
23
+ ordinary reconnect/renewal does not acquire new configuration dependencies.
24
+ No participant settings have been modified.
25
+
26
+ ## User 0.3.3 published — 2026-11-09
27
+
28
+ Tag v0.3.3 identifies c5f3be1. GitHub run 42567636609 passed behavior, standalone
29
+ archive and OIDC publication steps. Independent public-registry verification
30
+ confirmed exact Wallet 0.3.2 / Protocol 0.14.3 dependencies, provenance metadata
31
+ and byte-identical archive (SHA-256
32
+ aa231297a4690117850f5d50414d89ad0da86b29c29d03947383c23bfa62a833).
33
+ Participant directory-update acceptance remains pending. Existing directory installs
34
+ older than 0.3.3 need one npm install of 0.3.3 and repair before using local update.
35
+ HTTPS endpoint migration is not included.
36
+
37
+ ## User 0.3.3 release candidate — 2026-11-09
38
+
39
+ Release directory-local npm updates, keeping exact Wallet 0.3.2 and Protocol
40
+ 0.14.3 dependencies and the reviewed official test package unchanged. No endpoint
41
+ migration, Policy or saved request rewrite is included. All 383 local behavior tests and independent archive installation/process checks
42
+ passed. Tag-triggered publication and registry verification remain pending.
43
+
44
+ ## HTTPS endpoint migration — 2026-11-09
45
+
46
+ HTTPS migration inventory: existing setup rejects URL differences; local service settings, Market receipt origin/session, recovery observations and cancellation intents need coordinated handling. Public configuration publication does not migrate existing users. Keep original requests/Policies and current tunnels until an explicit local migration is implemented and verified.
47
+
48
+ ## Directory npm update — 2026-11-08
49
+
50
+ Update now supports a direct registry dependency under a project node_modules as
51
+ well as a global installation. It derives the owning project from the running
52
+ package, pins the confirmed version, preserves the dependency section, updates the
53
+ local lockfile and uses the existing stop/repair/private-data flow. Source/link,
54
+ custom-source and transitive installs are refused before changing services.
55
+ Nine updater regression checks passed. A real public-npm 0.3.1 → 0.3.2 upgrade
56
+ in a disposable project verified program/manifest/lockfile agreement and preserved
57
+ private-record fixtures; service hooks were controlled, not participant processes.
58
+ This exposed npm dry-run rewriting the hidden installed-tree lockfile. Preflight
59
+ now runs in a disposable prefix with private project npm settings, removed afterward.
60
+ See [verification](doc/records/local-npm-update-2026-11-08.md).
61
+ User 0.3.3 is the release candidate for this fix; publication verification is pending.
62
+
63
+ ## User 0.3.2 tag publication verified — 2026-11-08
64
+
65
+ Tag `v0.3.2` points to `890171b`; GitHub push-triggered OIDC run
66
+ [42491753287](https://github.com/c4rO-0/agentpay-user/actions/runs/42491753287)
67
+ completed successfully, including registry integrity verification. Public npm latest
68
+ is 0.3.2, pins Wallet 0.3.2 / Protocol 0.14.3 and includes provenance. Independently
69
+ downloaded registry bytes exactly match the locally tested archive (SHA-256
70
+ `e6bb2f1199e510850313c004055afeb3ba45afc07035697c1d37af14ebcf1489`).
71
+ All 380 existing tests and the independent package's real Wallet/Runtime restart
72
+ check passed. Participant npm update/restart acceptance remains the next step;
73
+ publication did not modify any participant services or create a paid Order.
74
+
3
75
  ## User 0.3.2 release pairing — 2026-11-08
4
76
 
5
77
  Candidate now pins the verified public Wallet 0.3.2 with Protocol 0.14.3; package,
@@ -13,6 +13,7 @@ import { checkServiceDirectory } from "./local-services.mjs";
13
13
  import { localWalletAdmin } from "./local-wallet-ipc.mjs";
14
14
  import { repairMarketWallet } from "./repair-market-wallet.mjs";
15
15
  import { controlWallet } from "./wallet-control.mjs";
16
+ import { preparePlatformEndpoints } from "./platform-endpoints.mjs";
16
17
  import { fetchJsonWithTimeout } from "./transport.mjs";
17
18
  export function marketOrigin(value) {
18
19
  const u = new URL(value);
@@ -129,6 +130,7 @@ export async function connectMarket(
129
130
  save = saveConnection,
130
131
  repair = repairMarketWallet,
131
132
  unlock = controlWallet,
133
+ prepareEndpoints = preparePlatformEndpoints,
132
134
  } = {},
133
135
  ) {
134
136
  let token, origin, completion;
@@ -194,6 +196,8 @@ export async function connectMarket(
194
196
  return 0;
195
197
  }
196
198
  origin = marketOrigin(answer.trim() || previous?.marketOrigin);
199
+ write("Checking Market and platform addresses…");
200
+ const changingOrigin = !!previous?.marketOrigin && marketOrigin(previous.marketOrigin) !== origin;
197
201
  const call = async (path, body, auth = true) => {
198
202
  const { response, result } = await fetchJson(
199
203
  origin + "/market" + path,
@@ -244,6 +248,8 @@ export async function connectMarket(
244
248
  token = signedIn.sessionToken;
245
249
  if (typeof token !== "string" || !token || !signedIn.developer?.id)
246
250
  throw Error("Market returned an invalid sign-in response.");
251
+ if (changingOrigin && previous.accountId !== signedIn.developer.id)
252
+ throw Error("This is not your saved Market account. The previous connection and addresses were kept.");
247
253
  write(
248
254
  "\n------------------------------------------------------------\n2/3 Name your Wallet\n------------------------------------------------------------",
249
255
  );
@@ -257,6 +263,19 @@ export async function connectMarket(
257
263
  String(w[key]).toLowerCase() === scope[key].toLowerCase(),
258
264
  ),
259
265
  );
266
+ // These authenticated record checks detect accidental cross-Market moves.
267
+ // They are not a cryptographic server identity; the user chooses the URL.
268
+ const originalConnectionMatches = !!previous?.connection?.id && !!existing &&
269
+ ["network", "module", "safe", "delegate"].every(key =>
270
+ typeof previous.connection[key] === "string" &&
271
+ previous.connection[key].toLowerCase() === String(existing[key]).toLowerCase()) &&
272
+ existing.policies?.some(p => p.id === previous.connection.id &&
273
+ String(p.policyId).toLowerCase() === String(previous.connection.policyId).toLowerCase());
274
+ if (changingOrigin && !originalConnectionMatches)
275
+ throw Error("Your original Wallet connection was not found at this address. The previous connection and addresses were kept.");
276
+ const endpoints = previous ? await prepareEndpoints({dataDir,origin,scope,fetchJson,changingOrigin}) : {changed:false,apply:async()=>{}};
277
+ if (endpoints.changed && (previous?.accountId !== signedIn.developer.id || !originalConnectionMatches))
278
+ throw Error("Platform addresses changed, but your original account and Wallet connection could not be confirmed. Current addresses were kept.");
260
279
  if (existing) write(`Using your Wallet name: ${existing.label}`);
261
280
  else
262
281
  write(
@@ -307,6 +326,10 @@ export async function connectMarket(
307
326
  write(
308
327
  "View this Wallet’s Orders and request help on Market. No spending permission is added.",
309
328
  );
329
+ if (endpoints.changed) write(`Also update platform addresses:
330
+ Settlement: ${endpoints.urls.settlementUrl}
331
+ Downloads: ${endpoints.urls.marketBundleMetadataUrl}
332
+ Runtime will restart; your spending permission stays the same.`);
310
333
  while (true) {
311
334
  const choice = (
312
335
  await ask(
@@ -345,17 +368,24 @@ export async function connectMarket(
345
368
  throw Error("Connection execution address differs from this Wallet.");
346
369
  if (result.connection.label !== label)
347
370
  throw Error("Connection nickname differs. Check Market Settings.");
348
- try {
349
- await save(dataDir, {
350
- version: 1,
351
- marketOrigin: origin,
352
- accountId: signedIn.developer.id,
353
- connection: result.connection,
354
- });
355
- } catch {
356
- write(
357
- "Market connected this Wallet, but the local shortcut could not be saved. Use --market URL next time.",
358
- );
371
+ if (changingOrigin && previous.connection.policyId === scope.policyId &&
372
+ result.connection.id !== previous.connection.id)
373
+ throw Error("Market returned a different connection record. The previous local connection was kept.");
374
+ const saveConnected = () => save(dataDir, {
375
+ version: 1,
376
+ marketOrigin: origin,
377
+ accountId: signedIn.developer.id,
378
+ connection: result.connection,
379
+ });
380
+ if (endpoints.changed) {
381
+ await endpoints.apply({write,saveConnection:saveConnected});
382
+ } else {
383
+ try { await saveConnected(); }
384
+ catch {
385
+ if (changingOrigin)
386
+ throw Error("The updated Market connection could not be saved. Retry the same command.");
387
+ write("Market connected this Wallet, but the local shortcut could not be saved. Use --market URL next time.");
388
+ }
359
389
  }
360
390
  completion = `\nWallet connected: ${label}\nView this Wallet’s Orders in Market → Account → My Orders.\nManage or disconnect it in Market Settings. This guide has finished.`;
361
391
  return 0;
@@ -1,11 +1,11 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
- // Update only a real global npm installation. Source checkouts/npm links are never
2
+ // Update real global or project npm installations. Source checkouts/npm links are never
3
3
  // overwritten. npm selects User; User's exact dependencies select Wallet/Protocol.
4
4
  import { execFile } from 'node:child_process';
5
5
  import { promisify } from 'node:util';
6
- import { access, appendFile, lstat, mkdir, open, readFile, realpath, unlink } from 'node:fs/promises';
6
+ import { access, appendFile, copyFile, chmod, lstat, mkdir, mkdtemp, open, readFile, realpath, rm, unlink } from 'node:fs/promises';
7
7
  import { constants } from 'node:fs';
8
- import { dirname, isAbsolute, join } from 'node:path';
8
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
9
9
  import { randomUUID } from 'node:crypto';
10
10
  import { createInterface } from 'node:readline/promises';
11
11
  import { installedUserDir, resolveUserDataDir, rememberUserDataDir } from './local-data-directory.mjs';
@@ -35,6 +35,35 @@ async function askConfirmation(message) {
35
35
  const terminal = createInterface({ input:process.stdin, output:process.stdout });
36
36
  try { return await terminal.question(message + '\n> '); } finally { terminal.close(); }
37
37
  }
38
+ // Resolve the installed package, not the terminal's working directory. A project
39
+ // update must name a direct dependency; never promote a transitive package or
40
+ // replace a linked source checkout as a side effect of updating User.
41
+ async function installationTarget(userDir, npm) {
42
+ const root = await npm(['root', '--global']);
43
+ if (!isAbsolute(root)) throw Error('npm installation paths could not be verified.');
44
+ const globalPath = join(root, name);
45
+ const info = await lstat(globalPath).catch(e => { if (e.code !== 'ENOENT') throw e; return null; });
46
+ if (info?.isDirectory() && !info.isSymbolicLink() && await realpath(globalPath) === await realpath(userDir)) {
47
+ const prefix = await npm(['prefix', '--global']);
48
+ if (!isAbsolute(prefix)) throw Error('npm installation paths could not be verified.');
49
+ return {prefix, flags:['--global'], directory:globalPath};
50
+ }
51
+ const scope = dirname(userDir), modules = dirname(scope), prefix = dirname(modules);
52
+ if (basename(userDir) !== 'agentpay-user' || basename(scope) !== '@c4roa0' || basename(modules) !== 'node_modules' ||
53
+ !(await lstat(userDir)).isDirectory() || (await lstat(userDir)).isSymbolicLink())
54
+ throw Error('This is a source checkout or linked installation. Update its files, run npm ci, then agentpay repair.');
55
+ const project = JSON.parse(await readFile(join(prefix,'package.json'),'utf8'));
56
+ const sections = ['dependencies','devDependencies','optionalDependencies'].filter(key => Object.hasOwn(project[key] || {}, name));
57
+ if (sections.length !== 1 || typeof project[sections[0]][name] !== 'string')
58
+ throw Error('User must be a direct npm dependency of this folder. Install it in a dedicated folder before using update.');
59
+ const spec = project[sections[0]][name];
60
+ if (/[:/\\]/.test(spec) || spec.startsWith('.'))
61
+ throw Error('This dependency uses a custom source. Update that source explicitly, then run agentpay repair.');
62
+ const save = {dependencies:'--save-prod',devDependencies:'--save-dev',optionalDependencies:'--save-optional'}[sections[0]];
63
+ return {prefix, directory:userDir, section:sections[0], spec,
64
+ flags:['--global=false','--workspaces=false','--save-exact',save,'--include=dev','--include=optional']};
65
+ }
66
+
38
67
  export async function updateFromNpm({
39
68
  userDir = installedUserDir, dataDir, force = false, write = console.log,
40
69
  ask = askConfirmation,
@@ -45,17 +74,13 @@ export async function updateFromNpm({
45
74
  } = {}) {
46
75
  let lock, lockPath, logPath, installing = false;
47
76
  try {
77
+ userDir = resolve(userDir);
48
78
  const current = JSON.parse(await readFile(join(userDir, 'package.json'), 'utf8'));
49
79
  if (current.name !== name || !stable(current.version)) throw Error('Unrecognized User installation.');
50
80
  write('Checking for a User update…');
51
- const root = await npm(['root','--global']), prefix = await npm(['prefix','--global']);
52
- if (!isAbsolute(root) || !isAbsolute(prefix)) throw Error('npm installation paths could not be verified.');
53
- const globalPath = join(root, name);
54
- const globalInfo = await lstat(globalPath).catch(e => { if(e.code !== 'ENOENT') throw e; return null; });
55
- if (!globalInfo || globalInfo.isSymbolicLink() || await realpath(globalPath) !== await realpath(userDir))
56
- throw Error('This is a source checkout or linked installation. Update its files, run npm ci, then agentpay repair.');
57
- await access(dirname(globalPath), constants.W_OK);
58
- const metadata = JSON.parse(await npm(['view',name + '@latest','--global','--json']));
81
+ const target = await installationTarget(userDir, npm);
82
+ await access(dirname(target.directory), constants.W_OK);
83
+ const metadata = JSON.parse(await npm(['view',name + '@latest','--json'], {cwd:target.prefix}));
59
84
  if (metadata.name !== name || !stable(metadata.version) ||
60
85
  !stable(metadata.dependencies?.['@c4roa0/agentpay-wallet']) ||
61
86
  !stable(metadata.dependencies?.['@c4roa0/agentpay-protocol']))
@@ -67,6 +92,7 @@ export async function updateFromNpm({
67
92
  return 0;
68
93
  }
69
94
  write(`User ${current.version} → ${metadata.version} · Wallet ${metadata.dependencies['@c4roa0/agentpay-wallet']}`);
95
+ write(`Installation folder: ${target.prefix}`);
70
96
  write('Background services will pause. Keys, permissions, Orders and cache are kept.');
71
97
  if (!force && (await ask("Type 'confirm' to update, or 'q' to cancel")).trim().toLowerCase() !== 'confirm') {
72
98
  write('Update cancelled. Nothing was changed.'); return 0;
@@ -81,12 +107,28 @@ export async function updateFromNpm({
81
107
  const rechecked = JSON.parse(await readFile(join(userDir,'package.json'),'utf8'));
82
108
  if (rechecked.version !== current.version || rechecked.name !== name)
83
109
  throw Error('Program files changed while reviewing the update. Run agentpay update again.');
110
+ if (target.section) {
111
+ const project = JSON.parse(await readFile(join(target.prefix,'package.json'),'utf8'));
112
+ if (project[target.section]?.[name] !== target.spec)
113
+ throw Error('The project dependency changed while reviewing the update. Run agentpay update again.');
114
+ }
84
115
  const env = {...process.env,AGENTPAY_USER_DATA_DIR:dataDir};
85
- const options = {logPath,env,cwd:stateDir,timeout:600000};
116
+ const options = {logPath,env,cwd:target.prefix,timeout:600000};
86
117
  const spec = name + '@' + metadata.version;
87
- const args = ['install','--global','--prefix',prefix,'--engine-strict','--ignore-scripts','--no-audit','--no-fund',spec];
118
+ const args = ['install',...target.flags,'--prefix',target.prefix,'--engine-strict','--ignore-scripts','--no-audit','--no-fund',spec];
88
119
  write('1/3 Check the update requirements.');
89
- await npm([...args,'--dry-run'],options);
120
+ // npm dry-run may rewrite its hidden installed-tree lockfile. Preflight in
121
+ // a disposable prefix, never the live installation. Carry project npm settings
122
+ // privately so registry/auth selection remains consistent with the real install.
123
+ const preflight = await mkdtemp(join(stateDir,'update-check-'));
124
+ try {
125
+ await copyFile(join(target.prefix,'.npmrc'),join(preflight,'.npmrc')).then(
126
+ () => chmod(join(preflight,'.npmrc'),0o600),
127
+ error => { if(error.code !== 'ENOENT') throw error; });
128
+ await npm(['install','--global=false','--prefix',preflight,'--engine-strict',
129
+ '--ignore-scripts','--no-audit','--no-fund',spec,'--dry-run'],
130
+ {...options,cwd:preflight});
131
+ } finally { await rm(preflight,{recursive:true,force:true}); }
90
132
  let configured = false;
91
133
  try { configured = (await lstat(join(dataDir,'runtime/local-services.json'))).isFile(); }
92
134
  catch(e) { if(e.code !== 'ENOENT') throw e; }
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: MPL-2.0
2
2
  // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
3
3
  // If a copy was not distributed with this file, see https://mozilla.org/MPL/2.0/.
4
+ import { sameSettlementAddress } from "./settlement-address-history.mjs";
4
5
  import { isUnpaidExpiry } from "./order-closure.mjs";
5
6
  import { randomBytes, createHash } from "node:crypto";
6
7
  import { constants } from "node:fs";
@@ -109,7 +110,7 @@ async function readIntent(ctx) {
109
110
  !B32.test(intent.cancellationId || "") ||
110
111
  intent.orderId !== ctx.orderId ||
111
112
  intent.policyId !== ctx.policyId ||
112
- intent.settlementUrl !== ctx.settlementUrl ||
113
+ !(await sameSettlementAddress(ctx.dataDir,intent.settlementUrl,ctx.settlementUrl,ctx.policyId)) ||
113
114
  !Number.isSafeInteger(intent.createdAtMs) ||
114
115
  intent.createdAtMs < 0
115
116
  )
@@ -0,0 +1,129 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ // Deliberate transport changes only. Never replace a Policy, request, or result.
3
+ import {open,mkdir,lstat,rename,unlink,readFile} from 'node:fs/promises';
4
+ import {constants} from 'node:fs';
5
+ import {join,dirname} from 'node:path';
6
+ import {homedir} from 'node:os';
7
+ import {randomUUID} from 'node:crypto';
8
+ import {execFile} from 'node:child_process';
9
+ import {promisify} from 'node:util';
10
+ import {installedUserDir} from './local-data-directory.mjs';
11
+ import {deploymentFilePath,installationDirectory} from './installation-paths.mjs';
12
+ import {readPrivateJson,deploymentChanges} from './deployment-status.mjs';
13
+ import {validatePublicDeployment} from './prepare-onboarding.mjs';
14
+ import {readServiceSettings,checkServiceDirectory,listenerPresent} from './local-services.mjs';
15
+ import {isManagedUnit} from './autostart-files.mjs';
16
+ import {fetchJsonWithTimeout} from './transport.mjs';
17
+ import {settlementProfileBlocker} from './readiness-profile.mjs';
18
+ import {readRuntimeStatus} from './runtime-status.mjs';
19
+ import {readSettlementAddresses} from './settlement-address-history.mjs';
20
+
21
+ const execute=promisify(execFile);
22
+ const normalize=value=>new URL(value).href.replace(/\/+$/,'');
23
+ const same=(a,b)=>JSON.stringify(a)===JSON.stringify(b);
24
+ const pendingPath=dir=>join(dir,'runtime/endpoint-update.pending.json');
25
+ const aliasesPath=dir=>join(dir,'runtime/settlement-addresses.json');
26
+
27
+ async function durableJson(path,value){
28
+ const temp=path+'.'+randomUUID()+'.tmp';let file;
29
+ try{
30
+ file=await open(temp,'wx',0o600);await file.writeFile(JSON.stringify(value)+'\n');await file.sync();await file.close();file=null;
31
+ await rename(temp,path);
32
+ const directory=await open(dirname(path),constants.O_RDONLY|constants.O_DIRECTORY|constants.O_NOFOLLOW);
33
+ try{await directory.sync();}finally{await directory.close();}
34
+ }finally{await file?.close();await unlink(temp).catch(e=>{if(e.code!=='ENOENT')throw e;});}
35
+ }
36
+
37
+
38
+ async function runtimeControl(action,{dataDir,settings}){
39
+ if(process.platform!=='linux')throw Error('Automatic address updates currently require managed Linux services. Keep your saved settings.');
40
+ const unit='agentpay-local-runtime.service',path=join(homedir(),'.config/systemd/user',unit);
41
+ const stat=await lstat(path);
42
+ if(!stat.isFile()||stat.isSymbolicLink()||stat.uid!==process.getuid()||(stat.mode&0o022)||!isManagedUnit(await readFile(path,'utf8'),unit,dataDir))
43
+ throw Error('Runtime is not managed by this User installation. Run agentpay repair before updating addresses.');
44
+ const ctl=async args=>(await execute('systemctl',['--user',...args],{timeout:45000,maxBuffer:16384})).stdout.trim();
45
+ if(await ctl(['show',unit,'--property=FragmentPath','--value'])!==path||await ctl(['show',unit,'--property=DropInPaths','--value']))
46
+ throw Error('Runtime service settings need review before updating addresses.');
47
+ await ctl([action,unit]);
48
+ if(action==='stop'){
49
+ const url=new URL(settings.runtimeUrl);
50
+ if(await listenerPresent({host:url.hostname,port:Number(url.port)}))throw Error('Runtime is still running. No connection settings were changed.');
51
+ }else{
52
+ for(let i=0;i<30;i++){
53
+ try{await readRuntimeStatus(dataDir);return;}catch{await new Promise(r=>setTimeout(r,500));}
54
+ }
55
+ throw Error('Runtime did not become ready. Retry agentpay market connect with the same Market address.');
56
+ }
57
+ }
58
+
59
+ export async function preparePlatformEndpoints({dataDir,origin,scope,userDir=installedUserDir,fetchJson=fetchJsonWithTimeout,changingOrigin=false}){
60
+ await checkServiceDirectory(dataDir);
61
+ const pending=await readPrivateJson(pendingPath(dataDir));
62
+ // Ordinary reconnect/renewal keeps its existing behavior. Only a deliberately
63
+ // changed origin or an interrupted update owns transport changes.
64
+ if(!changingOrigin&&!pending)return {changed:false,apply:async()=>{}};
65
+ const setup=await readPrivateJson(await deploymentFilePath(dataDir,userDir,'anvil-onboarding.json'));
66
+ if(!setup)throw Error('Local setup settings are missing. Run agentpay setup before connecting Market.');
67
+ const policy=await readPrivateJson(join(dataDir,'wallet/policy.json'));
68
+ if(!policy||policy.policyId?.toLowerCase()!==scope.policyId?.toLowerCase()||policy.safe?.toLowerCase()!==scope.safe?.toLowerCase()||`eip155:${policy.chainId}`!==scope.network||policy.module?.toLowerCase()!==scope.module?.toLowerCase())
69
+ throw Error('Wallet authorization changed. Run agentpay market connect again.');
70
+ const connectionBefore=await readPrivateJson(join(dataDir,'market-connection.json'));
71
+ if(pending&&(pending.version!==1||pending.origin!==origin||pending.policyId!==policy.policyId))
72
+ throw Error('Finish the pending address update with the same Policy. Retry: agentpay market connect --market '+pending.origin);
73
+ const {response,result}=await fetchJson(origin+'/market/web/onboarding/user-config',{method:'GET'},15000);
74
+ if(!response.ok)throw Error('Market settings could not be checked. Your current addresses are kept.');
75
+ const proposed=validatePublicDeployment(result);
76
+ const settings=await readServiceSettings(dataDir);
77
+ const urls={settlementUrl:proposed.settlementUrl,marketBundleMetadataUrl:proposed.marketBundleMetadataUrl};
78
+ const changed=Object.keys(urls).some(key=>normalize(setup[key])!==normalize(urls[key]))||normalize(settings.settlementUrl)!==normalize(urls.settlementUrl)||!!pending;
79
+ // RPC is participant-owned: a different local tunnel must never be overwritten.
80
+ // A Market connection alone is not authority to rotate contracts or test packages.
81
+ const publicPath=await deploymentFilePath(dataDir,userDir,'user-deployment.json');
82
+ const previousPublic=await readPrivateJson(publicPath);
83
+ if(!changed&&!changingOrigin)return {changed:false,apply:async()=>{}};
84
+ if(deploymentChanges(policy,proposed).length||deploymentChanges(setup,proposed).length||setup.mode!==proposed.mode||setup.tokenDecimals!==proposed.tokenDecimals||setup.tokenSymbol!==proposed.tokenSymbol)
85
+ throw Error('This Market uses different network or contract settings. Address update stopped; your current Policy and Orders are kept.');
86
+
87
+ // Query an unknown service to inspect deployment identity without depending on
88
+ // a particular Provider being online, or granting it an Order.
89
+ const query=new URL(normalize(urls.settlementUrl)+'/agentpay/purchase-readiness');
90
+ query.searchParams.set('skillId','0x'+'0'.repeat(64));query.searchParams.set('profileHash','0x'+'0'.repeat(64));
91
+ const readiness=await fetchJson(query.href,{method:'GET'},15000);
92
+ if(!readiness.response.ok||!readiness.result?.ok||settlementProfileBlocker(policy,readiness.result.result?.settlementProfile))
93
+ throw Error('The new Settlement address does not match this Wallet deployment. Current addresses are kept.');
94
+ const metadata=await fetchJson(urls.marketBundleMetadataUrl,{method:'GET'},15000);
95
+ if(!metadata.response.ok||!metadata.result?.ok||!/^[a-f0-9]{64}$/.test(metadata.result.result?.sha256||''))
96
+ throw Error('The new package address could not be checked. Current addresses are kept.');
97
+ if(!changed)return {changed:false,apply:async()=>{}};
98
+ return {changed:true,urls,async apply({write=console.log,manage=runtimeControl,stateDir=installationDirectory(),saveConnection=async()=>{}}={}){
99
+ await mkdir(stateDir,{recursive:true,mode:0o700});
100
+ const stat=await lstat(stateDir);
101
+ if(!stat.isDirectory()||stat.isSymbolicLink()||stat.uid!==process.getuid()||(stat.mode&0o077))throw Error('Unsafe installation state directory.');
102
+ const lockPath=join(stateDir,'install.lock');let lock;
103
+ try{lock=await open(lockPath,'wx',0o600);}catch(e){if(e.code==='EEXIST')throw Error('Another setup or update is running. Retry market connect when it finishes.');throw e;}
104
+ try{
105
+ if(!same(await readPrivateJson(join(dataDir,'market-connection.json')),connectionBefore)||!same(await readPrivateJson(join(dataDir,'wallet/policy.json')),policy)||!same(await readServiceSettings(dataDir),settings)||!same(await readPrivateJson(publicPath),previousPublic)||!same(await readPrivateJson(await deploymentFilePath(dataDir,userDir,'anvil-onboarding.json')),setup))
106
+ throw Error('Local settings changed during connection. Retry market connect; no addresses were changed.');
107
+ const aliases=await readSettlementAddresses(dataDir);
108
+ const entry={from:normalize(settings.settlementUrl),to:normalize(urls.settlementUrl),policyId:policy.policyId};
109
+ if(entry.from!==entry.to&&!aliases.entries.some(e=>same(e,entry))){
110
+ if(aliases.entries.length>=256)throw Error('Settlement address history needs review.');
111
+ aliases.entries.push(entry);
112
+ }
113
+ write('Updating platform addresses. Runtime will pause briefly; Wallet stays unlocked.');
114
+ // A durable marker makes interruptions retryable even after settings are written.
115
+ // Write the alias before the new active address; never mutate cancellation IDs.
116
+ await durableJson(pendingPath(dataDir),{version:1,origin,policyId:policy.policyId});
117
+ await saveConnection();
118
+ await manage('stop',{dataDir,settings});
119
+ await durableJson(aliasesPath(dataDir),aliases);
120
+ if(previousPublic)await durableJson(join(dataDir,'user-deployment.json'),{...previousPublic,...urls});
121
+ await durableJson(join(dataDir,'anvil-onboarding.json'),{...setup,...urls});
122
+ await durableJson(join(dataDir,'runtime/local-services.json'),{...settings,settlementUrl:normalize(urls.settlementUrl)});
123
+ await manage('start',{dataDir,settings:{...settings,settlementUrl:normalize(urls.settlementUrl)}});
124
+ await unlink(pendingPath(dataDir));
125
+ write('Platform addresses updated. Your Wallet, permission and Order records were kept.');
126
+ }catch(e){throw Error('Address update did not finish. Run agentpay market connect --market '+origin+' again. '+e.message);}
127
+ finally{await lock.close();await unlink(lockPath);}
128
+ }};
129
+ }
@@ -0,0 +1,28 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ // Local aliases acknowledge an explicitly confirmed address change, not extra
3
+ // authority. Cancellation IDs and Policy scopes remain unchanged.
4
+ import {join} from 'node:path';
5
+ import {readPrivateFile} from './runtime-client.mjs';
6
+ const normalize=value=>{
7
+ const u=new URL(value);
8
+ if(u.username||u.password||u.search||u.hash||!(u.protocol==='https:'||u.protocol==='http:'&&['localhost','127.0.0.1','[::1]'].includes(u.hostname)))throw Error('Invalid saved Settlement address.');
9
+ return u.href.replace(/\/+$/,'');
10
+ };
11
+ export async function readSettlementAddresses(dataDir){
12
+ let saved;
13
+ try{saved=JSON.parse(await readPrivateFile(join(dataDir,'runtime/settlement-addresses.json'),65536));}
14
+ catch(e){if(e.code==='ENOENT')return {version:1,entries:[]};throw e;}
15
+ if(saved?.version!==1||!Array.isArray(saved.entries)||saved.entries.length>256)throw Error('Invalid saved Settlement address history.');
16
+ for(const e of saved.entries){
17
+ if(!e||Object.keys(e).sort().join(',')!=='from,policyId,to'||!/^0x[a-fA-F0-9]{64}$/.test(e.policyId)||normalize(e.from)!==e.from||normalize(e.to)!==e.to)throw Error('Invalid saved Settlement address history.');
18
+ }
19
+ return saved;
20
+ }
21
+ export async function sameSettlementAddress(dataDir,from,to,policyId){
22
+ if(normalize(from)===normalize(to))return true;
23
+ const saved=await readSettlementAddresses(dataDir),seen=new Set([normalize(from)]);
24
+ for(let i=0;i<saved.entries.length;i++)for(const entry of saved.entries){
25
+ if(entry.policyId.toLowerCase()===policyId.toLowerCase()&&seen.has(entry.from))seen.add(entry.to);
26
+ }
27
+ return seen.has(normalize(to));
28
+ }
@@ -118,6 +118,16 @@ export async function readMarketStatus(dataDir) {
118
118
  await h?.close();
119
119
  }
120
120
  }
121
+ const endpointUpdate = await saved("runtime/endpoint-update.pending.json");
122
+ if (endpointUpdate) {
123
+ let origin;
124
+ try {
125
+ const url = new URL(endpointUpdate.origin);
126
+ if (url.origin === endpointUpdate.origin &&
127
+ (url.protocol === "https:" || url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))) origin = url.origin;
128
+ } catch {}
129
+ return {state:"address_pending", ...(origin ? {origin} : {})};
130
+ }
121
131
  const receipt = await saved("market-connection.json");
122
132
  if (!receipt) return { state: "none" };
123
133
  const policy = await saved("wallet/policy.json"),
@@ -409,7 +419,7 @@ export function renderStatusPanel(
409
419
  next =
410
420
  "Keep services running. Check progress in the original Agent conversation. If continuing a supported request, keep its original identifier; never create a replacement purchase.";
411
421
  }
412
- const marketPending = ["pending", "unknown"].includes(model.market?.state);
422
+ const marketPending = ["pending", "unknown", "address_pending"].includes(model.market?.state);
413
423
  if (marketPending && level === "ok") {
414
424
  title =
415
425
  model.market.state === "pending"
@@ -420,6 +430,12 @@ export function renderStatusPanel(
420
430
  "Website access to this Policy is not confirmed. Local payments are unaffected.";
421
431
  next = "agentpay market connect";
422
432
  }
433
+ if (model.market?.state === "address_pending") {
434
+ title = "Platform address update incomplete";
435
+ level = "wait";
436
+ detail = "Retry your Market connection to finish updating addresses. Original Order records are kept.";
437
+ next = "agentpay market connect" + (model.market.origin ? " --market " + model.market.origin : "");
438
+ }
423
439
  const divider = "-".repeat(Math.min(60, width));
424
440
  const lines = [
425
441
  "",
@@ -524,7 +540,9 @@ export function renderStatusPanel(
524
540
  lines.push("", "> Market");
525
541
  const state = model.market.state;
526
542
  lines.push(
527
- state === "saved"
543
+ state === "address_pending"
544
+ ? ` ${badge("wait")} Platform address update incomplete`
545
+ : state === "saved"
528
546
  ? ` ${badge("ok")} Current Policy connection saved locally`
529
547
  : state === "none"
530
548
  ? " Not connected from this installation"
package/update-pair.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "format": "agentpay.local-update-pair.v1",
3
- "userVersion": "0.3.2",
3
+ "userVersion": "0.3.4",
4
4
  "walletVersion": "0.3.2",
5
5
  "typesDependency": "0.14.3"
6
6
  }