@agfpd/iapeer 0.4.16 → 0.4.17
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 +14 -1
- package/package.json +1 -1
- package/src/cli/index.ts +76 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.4.17] - 2026-06-21
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `iapeer rename <old> <new>` — first-class peer-identity rename (parity with remove/create).
|
|
18
|
+
Renames the personality in **both** the registry and the per-cwd profile atomically and
|
|
19
|
+
**keeps the cwd**, so the claude transcript history (keyed by `realpath(cwd)`) survives.
|
|
20
|
+
Refuses a LIVE peer unless `--force` (a running session carries the old identity in its
|
|
21
|
+
env), and purges the old identity's lifecycle markers. Memory re-key (the personality-keyed
|
|
22
|
+
operativka folder + author/index attribution) is the memory provider's separate step;
|
|
23
|
+
native-memory is cwd-keyed and unaffected.
|
|
24
|
+
|
|
13
25
|
## [0.4.16] - 2026-06-21
|
|
14
26
|
|
|
15
27
|
### Fixed
|
|
@@ -245,7 +257,8 @@ Initial public release — the foundation core of the iapeer multi-agent ecosyst
|
|
|
245
257
|
- **Heterogeneous peers** — AI agents (Claude, Codex), humans (Telegram), and services
|
|
246
258
|
(timer = cron, watcher = event) are all first-class and addressed the same way.
|
|
247
259
|
|
|
248
|
-
[Unreleased]: https://github.com/agfpd/iapeer/compare/v0.4.
|
|
260
|
+
[Unreleased]: https://github.com/agfpd/iapeer/compare/v0.4.17...HEAD
|
|
261
|
+
[0.4.17]: https://github.com/agfpd/iapeer/compare/v0.4.16...v0.4.17
|
|
249
262
|
[0.4.16]: https://github.com/agfpd/iapeer/compare/v0.4.15...v0.4.16
|
|
250
263
|
[0.4.15]: https://github.com/agfpd/iapeer/compare/v0.4.14...v0.4.15
|
|
251
264
|
[0.4.14]: https://github.com/agfpd/iapeer/compare/v0.4.13...v0.4.14
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -53,7 +53,7 @@ import { getAdapter } from '../launch/index.ts'
|
|
|
53
53
|
import { hostRunDir } from '../launch/ptyHost.ts' // pty-only: attach via the supervisor client
|
|
54
54
|
import { runSupervisorClient } from '../supervisor/client.ts' // @xterm-free attach client (both reattach port-deps baked in)
|
|
55
55
|
import { cycleDaemon, isFoundationOwnedPlist, launchctlBootstrap, launchdLabel, launchdPlistPath } from '../launch/launchd.ts'
|
|
56
|
-
import { readPeerProfile, resolveCallerIdentity, resolveIdentity, writePeerProfileAtomic } from '../identity/index.ts'
|
|
56
|
+
import { readPeerProfile, renamePeer, resolveCallerIdentity, resolveIdentity, writePeerProfileAtomic } from '../identity/index.ts'
|
|
57
57
|
import { peerProfilePath } from '../storage/index.ts'
|
|
58
58
|
import {
|
|
59
59
|
isConformant,
|
|
@@ -827,6 +827,61 @@ export async function removePeerCli(
|
|
|
827
827
|
}
|
|
828
828
|
}
|
|
829
829
|
|
|
830
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
831
|
+
// rename — first-class peer-identity rename (parity with remove/create). Wraps
|
|
832
|
+
// renamePeer (registry + per-cwd profile, atomic, inside the lock) and KEEPS the
|
|
833
|
+
// cwd, so the claude transcript history (slug = realpath(cwd)) survives the rename.
|
|
834
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
835
|
+
|
|
836
|
+
export interface RenameOutcome {
|
|
837
|
+
oldPersonality: string
|
|
838
|
+
newPersonality: string
|
|
839
|
+
action: 'renamed' | 'absent' | 'target-exists' | 'refused-live'
|
|
840
|
+
reason?: string
|
|
841
|
+
cwd?: string
|
|
842
|
+
purgedState?: string[]
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Rename a peer's personality through renamePeer (registry + per-cwd profile, atomic;
|
|
847
|
+
* cwd KEPT — the transcript history keyed by slug=realpath(cwd) survives). Refuses a LIVE
|
|
848
|
+
* peer unless --force: a running session carries the OLD identity in its env
|
|
849
|
+
* (PEER_PERSONALITY, set at launch), so renaming under it desyncs the registry from the
|
|
850
|
+
* live process — stop it first, it respawns under the new identity. Purges the OLD
|
|
851
|
+
* identity-keyed lifecycle markers (remove-parity) so a future namesake can't inherit
|
|
852
|
+
* them. Does NOT move memory: the operativka folder + author/index attribution are
|
|
853
|
+
* personality-keyed (the memory provider re-keys them separately); native-memory is
|
|
854
|
+
* cwd-keyed and rides the kept cwd unchanged.
|
|
855
|
+
*/
|
|
856
|
+
export async function renamePeerCli(
|
|
857
|
+
oldPersonality: string,
|
|
858
|
+
newPersonality: string,
|
|
859
|
+
opts: CliEnvOptions & { force?: boolean } = {},
|
|
860
|
+
): Promise<RenameOutcome> {
|
|
861
|
+
const env = opts.env ?? process.env
|
|
862
|
+
const index = readPeersIndex({ env })
|
|
863
|
+
const peer = findPeer(index, oldPersonality)
|
|
864
|
+
if (!peer) return { oldPersonality, newPersonality, action: 'absent' }
|
|
865
|
+
if (findPeer(index, newPersonality)) {
|
|
866
|
+
return { oldPersonality, newPersonality, action: 'target-exists', reason: `"${newPersonality}" already exists` }
|
|
867
|
+
}
|
|
868
|
+
const cfg = loadLifecycleConfig(env)
|
|
869
|
+
if (!opts.force) {
|
|
870
|
+
const liveRt = peer.runtimes.find(rt => isPeerLive(rt, oldPersonality, cfg.sockDir))
|
|
871
|
+
if (liveRt) {
|
|
872
|
+
return {
|
|
873
|
+
oldPersonality,
|
|
874
|
+
newPersonality,
|
|
875
|
+
action: 'refused-live',
|
|
876
|
+
reason: `"${oldPersonality}" is LIVE on ${liveRt} — its running session carries the old identity in its env; stop it first (\`iapeer stop ${oldPersonality}\`) or pass --force`,
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
await renamePeer(oldPersonality, newPersonality, { env })
|
|
881
|
+
const purgedState = peer.runtimes.flatMap(rt => purgeIdentityState(cfg, buildProcessAddress(rt, oldPersonality)))
|
|
882
|
+
return { oldPersonality, newPersonality, action: 'renamed', cwd: peer.cwd, purgedState }
|
|
883
|
+
}
|
|
884
|
+
|
|
830
885
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
831
886
|
// send — manual IAP send fallback (contract Примитивы §send). Goes through the
|
|
832
887
|
// same router path as send_to_peer (resolve → deliver / wake), in-process so it
|
|
@@ -999,6 +1054,7 @@ const VERBS: ReadonlyArray<{ sig: string; desc: string }> = [
|
|
|
999
1054
|
desc: 'LAZY soft-reload: agentic peer comes up FRESH (re-reads doctrine) on its NEXT natural wake — no kill, no burst-wake; non-agentic runtimes skipped. --all = whole fleet. (eager: `new`/self-fresh)',
|
|
1000
1055
|
},
|
|
1001
1056
|
{ sig: 'remove <peer> [--force]', desc: 'delete a peer\'s registry record (locked writer); refuses a LIVE peer unless --force' },
|
|
1057
|
+
{ sig: 'rename <old> <new> [--force]', desc: 'rename a peer\'s personality (registry + per-cwd profile, atomic; cwd/transcript-history kept); refuses a LIVE peer unless --force. Memory re-key is the provider\'s separate step.' },
|
|
1002
1058
|
{
|
|
1003
1059
|
sig: 'send <target> (--message <text> | --message-file <f|->) [--from <id>] [--attachment <p>]… [--topic <t>]',
|
|
1004
1060
|
desc: 'manual IAP send (fallback)',
|
|
@@ -1702,6 +1758,25 @@ export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.en
|
|
|
1702
1758
|
else errOut(`remove: ${o.reason}\n`)
|
|
1703
1759
|
return o.action === 'refused-live' ? 1 : 0
|
|
1704
1760
|
}
|
|
1761
|
+
case 'rename': {
|
|
1762
|
+
// Rename a peer's personality (registry + per-cwd profile, atomic; cwd KEPT so
|
|
1763
|
+
// the transcript history survives). Refuses a LIVE peer unless --force. Memory
|
|
1764
|
+
// (operativka folder + author/index) is personality-keyed and re-keyed separately
|
|
1765
|
+
// by the provider — say so, or it reads as a silent data loss.
|
|
1766
|
+
if (!positionals[0] || !positionals[1]) {
|
|
1767
|
+
return argErr(errOut, 'rename needs old and new names — usage: iapeer rename <old> <new> [--force]')
|
|
1768
|
+
}
|
|
1769
|
+
const o = await renamePeerCli(positionals[0], positionals[1], { force: flags.force === true, env })
|
|
1770
|
+
if (o.action === 'renamed') {
|
|
1771
|
+
out(`renamed "${o.oldPersonality}" → "${o.newPersonality}" (registry + per-cwd profile; cwd kept: ${o.cwd})\n`)
|
|
1772
|
+
if (o.purgedState?.length) out(`lifecycle state purged (old identity): ${o.purgedState.join(', ')}\n`)
|
|
1773
|
+
out(
|
|
1774
|
+
`NOTE: memory is NOT moved by rename — re-key the operativka folder + author/index attribution under "${o.newPersonality}" via the memory provider (native-memory rides the kept cwd, unchanged). Wake "${o.newPersonality}" to verify identity + history.\n`,
|
|
1775
|
+
)
|
|
1776
|
+
} else if (o.action === 'absent') errOut(`rename: "${o.oldPersonality}" not registered\n`)
|
|
1777
|
+
else errOut(`rename: ${o.reason}\n`)
|
|
1778
|
+
return o.action === 'renamed' ? 0 : 1
|
|
1779
|
+
}
|
|
1705
1780
|
case 'send': {
|
|
1706
1781
|
// Message body from EITHER --message <text> OR --message-file <f> (f='-' →
|
|
1707
1782
|
// stdin). The runtime packages (telegram/notifier) + monitor deliver via
|