@agentmeshhq/agent 0.4.6 → 0.4.11

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.
Files changed (73) hide show
  1. package/dist/__tests__/auth-doctor-integration.test.d.ts +14 -0
  2. package/dist/__tests__/auth-doctor-integration.test.js +130 -0
  3. package/dist/__tests__/auth-doctor-integration.test.js.map +1 -0
  4. package/dist/__tests__/auth-guard.integration.test.d.ts +12 -0
  5. package/dist/__tests__/auth-guard.integration.test.js +132 -0
  6. package/dist/__tests__/auth-guard.integration.test.js.map +1 -0
  7. package/dist/__tests__/auth-guard.test.d.ts +17 -0
  8. package/dist/__tests__/auth-guard.test.js +483 -0
  9. package/dist/__tests__/auth-guard.test.js.map +1 -0
  10. package/dist/__tests__/done-state-guard.integration.test.d.ts +1 -0
  11. package/dist/__tests__/done-state-guard.integration.test.js +281 -0
  12. package/dist/__tests__/done-state-guard.integration.test.js.map +1 -0
  13. package/dist/__tests__/done-state-guard.test.d.ts +1 -0
  14. package/dist/__tests__/done-state-guard.test.js +327 -0
  15. package/dist/__tests__/done-state-guard.test.js.map +1 -0
  16. package/dist/__tests__/registry.register.test.d.ts +8 -0
  17. package/dist/__tests__/registry.register.test.js +109 -0
  18. package/dist/__tests__/registry.register.test.js.map +1 -0
  19. package/dist/__tests__/start-team-id.test.d.ts +9 -0
  20. package/dist/__tests__/start-team-id.test.js +160 -0
  21. package/dist/__tests__/start-team-id.test.js.map +1 -0
  22. package/dist/__tests__/tmux-runtime.test.d.ts +1 -0
  23. package/dist/__tests__/tmux-runtime.test.js +113 -0
  24. package/dist/__tests__/tmux-runtime.test.js.map +1 -0
  25. package/dist/cli/auth.d.ts +11 -0
  26. package/dist/cli/auth.js +92 -0
  27. package/dist/cli/auth.js.map +1 -0
  28. package/dist/cli/index.js +132 -1
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/cli/local.d.ts +4 -2
  31. package/dist/cli/local.js +257 -108
  32. package/dist/cli/local.js.map +1 -1
  33. package/dist/cli/migrate.d.ts +1 -0
  34. package/dist/cli/migrate.js +14 -10
  35. package/dist/cli/migrate.js.map +1 -1
  36. package/dist/cli/start.d.ts +11 -0
  37. package/dist/cli/start.js +46 -24
  38. package/dist/cli/start.js.map +1 -1
  39. package/dist/cli/test.d.ts +1 -0
  40. package/dist/cli/test.js +21 -10
  41. package/dist/cli/test.js.map +1 -1
  42. package/dist/cli/watcher.d.ts +27 -0
  43. package/dist/cli/watcher.js +365 -0
  44. package/dist/cli/watcher.js.map +1 -0
  45. package/dist/config/schema.d.ts +13 -0
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/core/auth-guard.d.ts +155 -0
  48. package/dist/core/auth-guard.js +498 -0
  49. package/dist/core/auth-guard.js.map +1 -0
  50. package/dist/core/auth-sync.d.ts +105 -0
  51. package/dist/core/auth-sync.js +263 -0
  52. package/dist/core/auth-sync.js.map +1 -0
  53. package/dist/core/daemon/context-template.js +65 -0
  54. package/dist/core/daemon/context-template.js.map +1 -1
  55. package/dist/core/daemon/done-state-guard.d.ts +63 -0
  56. package/dist/core/daemon/done-state-guard.js +102 -0
  57. package/dist/core/daemon/done-state-guard.js.map +1 -0
  58. package/dist/core/daemon/tmux-session.d.ts +1 -0
  59. package/dist/core/daemon/tmux-session.js +1 -1
  60. package/dist/core/daemon/tmux-session.js.map +1 -1
  61. package/dist/core/daemon.d.ts +18 -1
  62. package/dist/core/daemon.js +158 -37
  63. package/dist/core/daemon.js.map +1 -1
  64. package/dist/core/registry.d.ts +11 -1
  65. package/dist/core/registry.js +32 -1
  66. package/dist/core/registry.js.map +1 -1
  67. package/dist/core/tmux-runtime.d.ts +11 -2
  68. package/dist/core/tmux-runtime.js +45 -19
  69. package/dist/core/tmux-runtime.js.map +1 -1
  70. package/dist/core/tmux.d.ts +1 -1
  71. package/dist/core/tmux.js +7 -3
  72. package/dist/core/tmux.js.map +1 -1
  73. package/package.json +1 -1
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Integration test: auth-sync stale profile → repair → success
3
+ *
4
+ * Simulates the full Epic #470 recovery path:
5
+ * 1. Agent has a stale regular-file auth.json (not a symlink).
6
+ * 2. Canonical auth.json exists and is valid.
7
+ * 3. ensureAuthLink detects the stale file, backs it up, and creates a symlink.
8
+ * 4. preflightAuth now passes.
9
+ * 5. A second call to ensureAuthLink is idempotent (symlink-ok).
10
+ *
11
+ * All functions accept an optional `homeDir` parameter so tests can sandbox
12
+ * operations under a tmp directory instead of touching the real home directory.
13
+ */
14
+ export {};
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Integration test: auth-sync stale profile → repair → success
3
+ *
4
+ * Simulates the full Epic #470 recovery path:
5
+ * 1. Agent has a stale regular-file auth.json (not a symlink).
6
+ * 2. Canonical auth.json exists and is valid.
7
+ * 3. ensureAuthLink detects the stale file, backs it up, and creates a symlink.
8
+ * 4. preflightAuth now passes.
9
+ * 5. A second call to ensureAuthLink is idempotent (symlink-ok).
10
+ *
11
+ * All functions accept an optional `homeDir` parameter so tests can sandbox
12
+ * operations under a tmp directory instead of touching the real home directory.
13
+ */
14
+ import fs from "node:fs";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
18
+ import { ensureAuthLink, getAuthLinkStatus, preflightAuth } from "../core/auth-sync.js";
19
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
20
+ function makeTmpDir() {
21
+ return fs.mkdtempSync(path.join(os.tmpdir(), "auth-doctor-integration-"));
22
+ }
23
+ function agentAuthPathFor(base, agentName) {
24
+ return path.join(base, ".agentmesh", "opencode-data", agentName, "opencode", "auth.json");
25
+ }
26
+ function canonicalPathFor(base) {
27
+ return path.join(base, ".local", "share", "opencode", "auth.json");
28
+ }
29
+ // ─── Integration test ────────────────────────────────────────────────────────
30
+ describe("Auth stale-profile → repair → pass (integration)", () => {
31
+ let base;
32
+ let canonical;
33
+ const AGENT = "forge-dev-1";
34
+ beforeEach(() => {
35
+ base = makeTmpDir();
36
+ canonical = canonicalPathFor(base);
37
+ // Create canonical auth with valid provider entry
38
+ fs.mkdirSync(path.dirname(canonical), { recursive: true });
39
+ fs.writeFileSync(canonical, JSON.stringify({ anthropic: { token: "tok-abc123" } }), "utf-8");
40
+ fs.chmodSync(canonical, 0o600);
41
+ });
42
+ afterEach(() => {
43
+ fs.rmSync(base, { recursive: true, force: true });
44
+ });
45
+ it("repairs a stale regular-file and creates a correct symlink", () => {
46
+ const agentAuth = agentAuthPathFor(base, AGENT);
47
+ const agentDir = path.dirname(agentAuth);
48
+ // Plant a stale regular file (simulating drift — old token copy)
49
+ fs.mkdirSync(agentDir, { recursive: true });
50
+ fs.writeFileSync(agentAuth, JSON.stringify({ anthropic: { token: "STALE" } }), "utf-8");
51
+ // Verify the stale state
52
+ const stale = fs.lstatSync(agentAuth);
53
+ expect(stale.isFile()).toBe(true);
54
+ expect(stale.isSymbolicLink()).toBe(false);
55
+ // Run repair with sandboxed homeDir
56
+ const result = ensureAuthLink(AGENT, canonical, base);
57
+ expect(result.ok).toBe(true);
58
+ expect(result.action).toBe("symlink-replaced-file");
59
+ // Verify symlink now in place
60
+ const repaired = fs.lstatSync(agentAuth);
61
+ expect(repaired.isSymbolicLink()).toBe(true);
62
+ expect(fs.readlinkSync(agentAuth)).toBe(canonical);
63
+ // Verify backup exists
64
+ const backups = fs.readdirSync(agentDir).filter((f) => f.startsWith("auth.json.bak."));
65
+ expect(backups.length).toBe(1);
66
+ // Verify symlink resolves to valid canonical content
67
+ const content = JSON.parse(fs.readFileSync(agentAuth, "utf-8"));
68
+ expect(content.anthropic).toBeDefined();
69
+ // Token should now be from canonical, not stale
70
+ expect(content.anthropic.token).toBe("tok-abc123");
71
+ });
72
+ it("preflightAuth passes after repair", () => {
73
+ const agentAuth = agentAuthPathFor(base, AGENT);
74
+ const agentDir = path.dirname(agentAuth);
75
+ // Plant stale file, then explicitly repair it first (ensureAuthLink converts
76
+ // the stale regular-file to a symlink). preflightAuth auto-repairs only missing
77
+ // or broken symlinks — a readable regular file is considered linkOk by design.
78
+ fs.mkdirSync(agentDir, { recursive: true });
79
+ fs.writeFileSync(agentAuth, JSON.stringify({ anthropic: { token: "STALE" } }), "utf-8");
80
+ ensureAuthLink(AGENT, canonical, base);
81
+ // Now preflightAuth on the already-repaired state should pass cleanly
82
+ const result = preflightAuth(AGENT, canonical, base);
83
+ expect(result.passed).toBe(true);
84
+ expect(result.linkOk).toBe(true);
85
+ expect(result.canonicalOk).toBe(true);
86
+ expect(result.contentOk).toBe(true);
87
+ // Verify the symlink is in place at the sandboxed path
88
+ const lstat = fs.lstatSync(agentAuth);
89
+ expect(lstat.isSymbolicLink()).toBe(true);
90
+ expect(fs.readlinkSync(agentAuth)).toBe(canonical);
91
+ });
92
+ it("is idempotent — second ensureAuthLink call returns symlink-ok", () => {
93
+ const agentAuth = agentAuthPathFor(base, AGENT);
94
+ const agentDir = path.dirname(agentAuth);
95
+ // First: create symlink (homeDir sandboxed to base)
96
+ fs.mkdirSync(agentDir, { recursive: true });
97
+ const r1 = ensureAuthLink(AGENT, canonical, base);
98
+ expect(r1.ok).toBe(true);
99
+ expect(r1.action).toBe("symlink-created");
100
+ // Second: already correct symlink
101
+ const r2 = ensureAuthLink(AGENT, canonical, base);
102
+ expect(r2.ok).toBe(true);
103
+ expect(r2.action).toBe("symlink-ok");
104
+ // Exactly one symlink, no extra backups
105
+ const entries = fs.readdirSync(agentDir);
106
+ expect(entries.filter((f) => f.startsWith("auth.json.bak."))).toHaveLength(0);
107
+ });
108
+ it("handles missing canonical gracefully — returns no-op", () => {
109
+ const agentAuth = agentAuthPathFor(base, AGENT);
110
+ const agentDir = path.dirname(agentAuth);
111
+ fs.mkdirSync(agentDir, { recursive: true });
112
+ const missingCanonical = path.join(base, ".local", "share", "opencode", "DOES_NOT_EXIST.json");
113
+ const result = ensureAuthLink(AGENT, missingCanonical, base);
114
+ expect(result.ok).toBe(false);
115
+ expect(result.action).toBe("no-op");
116
+ });
117
+ it("getAuthLinkStatus reflects post-repair state accurately", () => {
118
+ const agentAuth = agentAuthPathFor(base, AGENT);
119
+ const agentDir = path.dirname(agentAuth);
120
+ // Plant stale file then repair (homeDir sandboxed to base)
121
+ fs.mkdirSync(agentDir, { recursive: true });
122
+ fs.writeFileSync(agentAuth, '{"old":true}', "utf-8");
123
+ ensureAuthLink(AGENT, canonical, base);
124
+ // Use getAuthLinkStatus with the same sandbox
125
+ const status = getAuthLinkStatus(AGENT, canonical, base);
126
+ expect(status.isSymlink).toBe(true);
127
+ expect(status.pointsToCanonical).toBe(true);
128
+ });
129
+ });
130
+ //# sourceMappingURL=auth-doctor-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-doctor-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/auth-doctor-integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Integration test: Auth drift -> doctor repair -> success
3
+ * Epic #470
4
+ *
5
+ * Simulates the real incident:
6
+ * 1. Agent starts with stale per-agent auth (regular file, old tokens)
7
+ * 2. Global auth is refreshed (new token written to canonical)
8
+ * 3. Doctor runs with --repair
9
+ * 4. Per-agent auth now points to fresh canonical via symlink
10
+ * 5. Subsequent reads see fresh token — drift eliminated
11
+ */
12
+ export {};
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Integration test: Auth drift -> doctor repair -> success
3
+ * Epic #470
4
+ *
5
+ * Simulates the real incident:
6
+ * 1. Agent starts with stale per-agent auth (regular file, old tokens)
7
+ * 2. Global auth is refreshed (new token written to canonical)
8
+ * 3. Doctor runs with --repair
9
+ * 4. Per-agent auth now points to fresh canonical via symlink
10
+ * 5. Subsequent reads see fresh token — drift eliminated
11
+ */
12
+ import fs from "node:fs";
13
+ import os from "node:os";
14
+ import path from "node:path";
15
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
16
+ import { isValidAuthSymlink, linkAgentAuth, validateCanonicalAuth, } from "../core/auth-guard.js";
17
+ function makeTmpDir() {
18
+ return fs.mkdtempSync(path.join(os.tmpdir(), "am-auth-integ-"));
19
+ }
20
+ function writeJson(filePath, data) {
21
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
22
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
23
+ }
24
+ describe("Auth drift -> repair -> success (integration)", () => {
25
+ let tmp;
26
+ let canonicalPath;
27
+ let agentAuthFile;
28
+ beforeEach(() => {
29
+ tmp = makeTmpDir();
30
+ canonicalPath = path.join(tmp, "global", "opencode", "auth.json");
31
+ agentAuthFile = path.join(tmp, "opencode-data", "forge-dev-1", "opencode", "auth.json");
32
+ });
33
+ afterEach(() => {
34
+ fs.rmSync(tmp, { recursive: true, force: true });
35
+ });
36
+ it("simulates stale regular file -> symlink repair -> fresh token visible", () => {
37
+ // Step 1: Agent was started before global auth was refreshed
38
+ // Per-agent auth.json exists as a regular file with OLD token material
39
+ const staleToken = { provider: "anthropic", accessToken: "old_token_STALE_400" };
40
+ writeJson(agentAuthFile, staleToken);
41
+ expect(fs.lstatSync(agentAuthFile).isSymbolicLink()).toBe(false);
42
+ // Step 2: Global auth is refreshed (user re-authenticated)
43
+ const freshToken = { provider: "anthropic", accessToken: "new_token_FRESH_OK" };
44
+ writeJson(canonicalPath, freshToken);
45
+ expect(validateCanonicalAuth(canonicalPath)).toBe(true);
46
+ // At this point, agent still sees the stale token (drift!)
47
+ const staleRead = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
48
+ expect(staleRead.accessToken).toBe("old_token_STALE_400");
49
+ // Step 3: Doctor/repair runs — replace regular file with symlink
50
+ const repairResult = linkAgentAuth(agentAuthFile, canonicalPath);
51
+ expect(repairResult.linked).toBe(true);
52
+ expect(repairResult.backedUp).toBeDefined();
53
+ expect(fs.lstatSync(agentAuthFile).isSymbolicLink()).toBe(true);
54
+ // Step 4: Post-repair — agent now sees fresh token via symlink
55
+ expect(isValidAuthSymlink(agentAuthFile)).toBe(true);
56
+ const freshRead = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
57
+ expect(freshRead.accessToken).toBe("new_token_FRESH_OK");
58
+ // Step 5: Any subsequent global auth refresh is immediately visible
59
+ const newerToken = { provider: "anthropic", accessToken: "newer_token_ALSO_OK" };
60
+ writeJson(canonicalPath, newerToken);
61
+ const newerRead = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
62
+ expect(newerRead.accessToken).toBe("newer_token_ALSO_OK");
63
+ // Backup of stale file preserved
64
+ const backedUpContent = JSON.parse(fs.readFileSync(repairResult.backedUp, "utf-8"));
65
+ expect(backedUpContent.accessToken).toBe("old_token_STALE_400");
66
+ });
67
+ it("symlink-first: new agent always gets fresh token immediately", () => {
68
+ // Global auth exists before agent ever starts
69
+ const freshToken = { provider: "anthropic", accessToken: "initial_fresh_token" };
70
+ writeJson(canonicalPath, freshToken);
71
+ // Agent profile dir created (simulates prepareOpenCodeRuntime)
72
+ fs.mkdirSync(path.dirname(agentAuthFile), { recursive: true });
73
+ // ensureAgentAuthLink creates the symlink (no prior file)
74
+ const result = linkAgentAuth(agentAuthFile, canonicalPath);
75
+ expect(result.linked).toBe(true);
76
+ expect(result.backedUp).toBeUndefined(); // nothing to back up
77
+ // Token is immediately visible
78
+ const read = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
79
+ expect(read.accessToken).toBe("initial_fresh_token");
80
+ // Update global -> agent sees it with zero extra steps
81
+ writeJson(canonicalPath, { provider: "anthropic", accessToken: "rotated_token" });
82
+ const read2 = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
83
+ expect(read2.accessToken).toBe("rotated_token");
84
+ });
85
+ it("multi-agent: all agents see same fresh token via individual symlinks", () => {
86
+ const agents = ["forge-dev-1", "forge-dev-2", "forge-dev-3"];
87
+ const freshToken = { provider: "anthropic", accessToken: "shared_fresh" };
88
+ writeJson(canonicalPath, freshToken);
89
+ // Set up symlinks for all agents
90
+ for (const agentName of agents) {
91
+ const authPath = path.join(tmp, "opencode-data", agentName, "opencode", "auth.json");
92
+ fs.mkdirSync(path.dirname(authPath), { recursive: true });
93
+ const res = linkAgentAuth(authPath, canonicalPath);
94
+ expect(res.linked).toBe(true);
95
+ }
96
+ // All see fresh token
97
+ for (const agentName of agents) {
98
+ const authPath = path.join(tmp, "opencode-data", agentName, "opencode", "auth.json");
99
+ const content = JSON.parse(fs.readFileSync(authPath, "utf-8"));
100
+ expect(content.accessToken).toBe("shared_fresh");
101
+ }
102
+ // Rotate global token — all agents immediately see new value
103
+ writeJson(canonicalPath, { provider: "anthropic", accessToken: "rotated_all" });
104
+ for (const agentName of agents) {
105
+ const authPath = path.join(tmp, "opencode-data", agentName, "opencode", "auth.json");
106
+ const content = JSON.parse(fs.readFileSync(authPath, "utf-8"));
107
+ expect(content.accessToken).toBe("rotated_all");
108
+ }
109
+ });
110
+ it("healthcheck watcher detects broken symlink and emits degraded event", async () => {
111
+ // Create initial valid symlink for agent
112
+ writeJson(canonicalPath, { provider: "anthropic", accessToken: "ok_token" });
113
+ fs.mkdirSync(path.dirname(agentAuthFile), { recursive: true });
114
+ linkAgentAuth(agentAuthFile, canonicalPath);
115
+ expect(isValidAuthSymlink(agentAuthFile)).toBe(true);
116
+ // Now simulate canonical being deleted (e.g., moved/rotated)
117
+ fs.unlinkSync(canonicalPath);
118
+ expect(isValidAuthSymlink(agentAuthFile)).toBe(false);
119
+ // Collect events from watcher — using a manual check rather than timers
120
+ // since we can't override AGENTMESH_OPENCODE_DATA_ROOT
121
+ // Test directly: broken symlink should be detected as invalid
122
+ const lstat = fs.lstatSync(agentAuthFile);
123
+ expect(lstat.isSymbolicLink()).toBe(true);
124
+ // Restore canonical
125
+ writeJson(canonicalPath, { provider: "anthropic", accessToken: "restored_token" });
126
+ expect(isValidAuthSymlink(agentAuthFile)).toBe(true);
127
+ // Agent now sees restored token
128
+ const content = JSON.parse(fs.readFileSync(agentAuthFile, "utf-8"));
129
+ expect(content.accessToken).toBe("restored_token");
130
+ });
131
+ });
132
+ //# sourceMappingURL=auth-guard.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-guard.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/auth-guard.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Unit tests for auth-guard.ts — Epics #470 + #490
3
+ *
4
+ * Tests:
5
+ * 1. validateCanonicalAuth — detects valid/invalid/missing canonical auth
6
+ * 2. isValidAuthSymlink — detects valid/broken/missing/regular-file symlinks
7
+ * 3. linkAgentAuth — creates symlink, backs up regular files, removes stale symlinks
8
+ * 4. ensureAgentAuthLink — full link-or-repair flow
9
+ * 5. preflightAgentAuth — preflight with retry semantics
10
+ * 6. startAuthHealthWatcher — periodic check, backoff, event emission
11
+ * 7. runAuthDoctor — discovery + optional repair
12
+ * 8. isTempPath — temp path detection (#490)
13
+ * 9. validateAuthSchema — schema validation incl. auth.type field (#490)
14
+ * 10. writeRealAuthFile — real-file fallback when symlinking fails (#490)
15
+ * 11. getSymlinkTempTarget — detects temp-path symlink targets (#490)
16
+ */
17
+ export {};