@camstack/core 0.1.35 → 0.1.37
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/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +21 -3
- package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +21 -3
- package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts.map +1 -1
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js +32 -1
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js.map +1 -1
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs +32 -1
- package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs.map +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts.map +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +16 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js.map +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +16 -0
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs.map +1 -1
- package/dist/index.js +173 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +173 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mesh-orchestrator.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,SAAS,
|
|
1
|
+
{"version":3,"file":"mesh-orchestrator.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,SAAS,EAKT,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAA;AAqCxB,qBAAa,qBAAsB,SAAQ,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;cAKzD,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAsC/D,OAAO,CAAC,WAAW;YAML,aAAa;CAqD5B;AAED,eAAe,qBAAqB,CAAA"}
|
|
@@ -29,6 +29,22 @@ var MeshOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
29
29
|
const impl = this.resolveImpl(addonId);
|
|
30
30
|
if (impl?.leave) await impl.leave();
|
|
31
31
|
return { success: true };
|
|
32
|
+
},
|
|
33
|
+
startLoginProvider: async ({ addonId, hostname }) => {
|
|
34
|
+
const impl = this.resolveImpl(addonId);
|
|
35
|
+
if (!impl?.startLogin) throw new Error(`Mesh provider "${addonId}" does not support startLogin`);
|
|
36
|
+
return await impl.startLogin(hostname ? { hostname } : {});
|
|
37
|
+
},
|
|
38
|
+
logoutProvider: async ({ addonId }) => {
|
|
39
|
+
const impl = this.resolveImpl(addonId);
|
|
40
|
+
if (!impl?.logout) throw new Error(`Mesh provider "${addonId}" does not support logout`);
|
|
41
|
+
await impl.logout();
|
|
42
|
+
return { loggedOut: true };
|
|
43
|
+
},
|
|
44
|
+
listProviderPeers: async ({ addonId }) => {
|
|
45
|
+
const impl = this.resolveImpl(addonId);
|
|
46
|
+
if (!impl?.listPeers) return { peers: [] };
|
|
47
|
+
return await impl.listPeers();
|
|
32
48
|
}
|
|
33
49
|
};
|
|
34
50
|
this.ctx.logger.info("Mesh orchestrator initialized");
|
|
@@ -50,6 +66,11 @@ var MeshOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
50
66
|
let peerCount = 0;
|
|
51
67
|
let endpoints = [];
|
|
52
68
|
let error;
|
|
69
|
+
let tenantName = "";
|
|
70
|
+
let magicDnsSuffix = "";
|
|
71
|
+
let userLogin = null;
|
|
72
|
+
let controlPlaneUrl = "";
|
|
73
|
+
let keyExpiry = null;
|
|
53
74
|
if (impl.getStatus) try {
|
|
54
75
|
const s = await impl.getStatus();
|
|
55
76
|
joined = s.joined;
|
|
@@ -58,6 +79,11 @@ var MeshOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
58
79
|
peerCount = s.peerCount;
|
|
59
80
|
endpoints = s.endpoints;
|
|
60
81
|
error = s.error;
|
|
82
|
+
tenantName = s.tenantName;
|
|
83
|
+
magicDnsSuffix = s.magicDnsSuffix;
|
|
84
|
+
userLogin = s.userLogin;
|
|
85
|
+
controlPlaneUrl = s.controlPlaneUrl;
|
|
86
|
+
keyExpiry = s.keyExpiry;
|
|
61
87
|
} catch (err) {
|
|
62
88
|
error = err instanceof Error ? err.message : String(err);
|
|
63
89
|
}
|
|
@@ -69,7 +95,12 @@ var MeshOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
69
95
|
magicDnsHostname,
|
|
70
96
|
peerCount,
|
|
71
97
|
endpoints,
|
|
72
|
-
...error !== void 0 ? { error } : {}
|
|
98
|
+
...error !== void 0 ? { error } : {},
|
|
99
|
+
tenantName,
|
|
100
|
+
magicDnsSuffix,
|
|
101
|
+
userLogin,
|
|
102
|
+
controlPlaneUrl,
|
|
103
|
+
keyExpiry
|
|
73
104
|
});
|
|
74
105
|
}
|
|
75
106
|
return out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mesh-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"sourcesContent":["/**\n * Mesh orchestrator — singleton facade over the `mesh-network`\n * collection. One row per provider (Tailscale, Headscale, ZeroTier),\n * aggregated for the admin UI's Mesh Networks page.\n */\nimport {\n BaseAddon,\n meshOrchestratorCapability,\n type IMeshOrchestrator,\n type MeshProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface
|
|
1
|
+
{"version":3,"file":"mesh-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"sourcesContent":["/**\n * Mesh orchestrator — singleton facade over the `mesh-network`\n * collection. One row per provider (Tailscale, Headscale, ZeroTier),\n * aggregated for the admin UI's Mesh Networks page.\n */\nimport {\n BaseAddon,\n meshOrchestratorCapability,\n type IMeshOrchestrator,\n type MeshPeer,\n type MeshProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface MeshStatusLike {\n joined: boolean\n meshIp: string\n magicDnsHostname: string\n peerCount: number\n endpoints: readonly {\n id: string\n label: string\n scope: 'mesh' | 'public'\n url: string\n hostname: string\n port: number\n protocol: 'http' | 'https'\n }[]\n error?: string\n tenantName: string\n magicDnsSuffix: string\n userLogin: string | null\n controlPlaneUrl: string\n keyExpiry: number | null\n}\n\ninterface MeshNetworkLike {\n getStatus?: () => Promise<MeshStatusLike>\n join?: (input: { authKey: string; hostname?: string }) => Promise<{ joined: true }>\n startLogin?: (input: { hostname?: string }) => Promise<{ loginUrl: string }>\n leave?: () => Promise<{ left: true }>\n logout?: () => Promise<{ loggedOut: true }>\n listPeers?: () => Promise<{ peers: readonly MeshPeer[] }>\n}\n\ninterface MeshRegistrationMeta {\n readonly displayName?: string\n}\n\nexport class MeshOrchestratorAddon extends BaseAddon<Record<string, never>> {\n constructor() {\n super({})\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IMeshOrchestrator = {\n listProviders: async () => this.listProviders(),\n joinProvider: async ({ addonId, authKey, hostname }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.join) throw new Error(`Mesh provider \"${addonId}\" does not support join`)\n return await impl.join({ authKey, ...(hostname ? { hostname } : {}) })\n },\n leaveProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.leave) await impl.leave()\n return { success: true as const }\n },\n startLoginProvider: async ({ addonId, hostname }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.startLogin) {\n throw new Error(`Mesh provider \"${addonId}\" does not support startLogin`)\n }\n return await impl.startLogin(hostname ? { hostname } : {})\n },\n logoutProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.logout) {\n throw new Error(`Mesh provider \"${addonId}\" does not support logout`)\n }\n await impl.logout()\n return { loggedOut: true as const }\n },\n listProviderPeers: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.listPeers) return { peers: [] }\n return await impl.listPeers()\n },\n }\n this.ctx.logger.info('Mesh orchestrator initialized')\n return [{ capability: meshOrchestratorCapability, provider }]\n }\n\n private resolveImpl(addonId: string): MeshNetworkLike | null {\n const entries = this.capabilities?.getCollectionEntries<MeshNetworkLike>('mesh-network') ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly MeshProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<MeshNetworkLike & MeshRegistrationMeta>(\n 'mesh-network',\n ) ?? []\n const out: MeshProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let joined = false\n let meshIp = ''\n let magicDnsHostname = ''\n let peerCount = 0\n let endpoints: MeshProviderInfo['endpoints'] = []\n let error: string | undefined\n let tenantName = ''\n let magicDnsSuffix = ''\n let userLogin: string | null = null\n let controlPlaneUrl = ''\n let keyExpiry: number | null = null\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n joined = s.joined\n meshIp = s.meshIp\n magicDnsHostname = s.magicDnsHostname\n peerCount = s.peerCount\n endpoints = s.endpoints\n error = s.error\n tenantName = s.tenantName\n magicDnsSuffix = s.magicDnsSuffix\n userLogin = s.userLogin\n controlPlaneUrl = s.controlPlaneUrl\n keyExpiry = s.keyExpiry\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n joined,\n meshIp,\n magicDnsHostname,\n peerCount,\n endpoints,\n ...(error !== undefined ? { error } : {}),\n tenantName,\n magicDnsSuffix,\n userLogin,\n controlPlaneUrl,\n keyExpiry,\n })\n }\n return out\n }\n}\n\nexport default MeshOrchestratorAddon\n"],"mappings":";;;;;;;;;;;;AAiDA,IAAa,wBAAb,cAA2C,gBAAA,UAAiC;CAC1E,cAAc;EACZ,MAAM,EAAE,CAAC;;CAGX,MAAgB,eAAgD;EAC9D,MAAM,WAA8B;GAClC,eAAe,YAAY,KAAK,eAAe;GAC/C,cAAc,OAAO,EAAE,SAAS,SAAS,eAAe;IACtD,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,MAAM,MAAM,IAAI,MAAM,kBAAkB,QAAQ,yBAAyB;IACpF,OAAO,MAAM,KAAK,KAAK;KAAE;KAAS,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;KAAG,CAAC;;GAExE,eAAe,OAAO,EAAE,cAAc;IACpC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,MAAM,OAAO,MAAM,KAAK,OAAO;IACnC,OAAO,EAAE,SAAS,MAAe;;GAEnC,oBAAoB,OAAO,EAAE,SAAS,eAAe;IACnD,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,YACT,MAAM,IAAI,MAAM,kBAAkB,QAAQ,+BAA+B;IAE3E,OAAO,MAAM,KAAK,WAAW,WAAW,EAAE,UAAU,GAAG,EAAE,CAAC;;GAE5D,gBAAgB,OAAO,EAAE,cAAc;IACrC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,QACT,MAAM,IAAI,MAAM,kBAAkB,QAAQ,2BAA2B;IAEvE,MAAM,KAAK,QAAQ;IACnB,OAAO,EAAE,WAAW,MAAe;;GAErC,mBAAmB,OAAO,EAAE,cAAc;IACxC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,WAAW,OAAO,EAAE,OAAO,EAAE,EAAE;IAC1C,OAAO,MAAM,KAAK,WAAW;;GAEhC;EACD,KAAK,IAAI,OAAO,KAAK,gCAAgC;EACrD,OAAO,CAAC;GAAE,YAAY,gBAAA;GAA4B;GAAU,CAAC;;CAG/D,YAAoB,SAAyC;EAG3D,QAFgB,KAAK,cAAc,qBAAsC,eAAe,IAAI,EAAE,EACxE,MAAM,CAAC,QAAQ,OAAO,QACrC,GAAQ,MAAM;;CAGvB,MAAc,gBAAsD;EAClE,MAAM,UAAU,KAAK,cAAc,qBACjC,eACD,IAAI,EAAE;EACP,MAAM,MAA0B,EAAE;EAClC,KAAK,MAAM,CAAC,SAAS,SAAS,SAAS;GACrC,IAAI,SAAS;GACb,IAAI,SAAS;GACb,IAAI,mBAAmB;GACvB,IAAI,YAAY;GAChB,IAAI,YAA2C,EAAE;GACjD,IAAI;GACJ,IAAI,aAAa;GACjB,IAAI,iBAAiB;GACrB,IAAI,YAA2B;GAC/B,IAAI,kBAAkB;GACtB,IAAI,YAA2B;GAC/B,IAAI,KAAK,WACP,IAAI;IACF,MAAM,IAAI,MAAM,KAAK,WAAW;IAChC,SAAS,EAAE;IACX,SAAS,EAAE;IACX,mBAAmB,EAAE;IACrB,YAAY,EAAE;IACd,YAAY,EAAE;IACd,QAAQ,EAAE;IACV,aAAa,EAAE;IACf,iBAAiB,EAAE;IACnB,YAAY,EAAE;IACd,kBAAkB,EAAE;IACpB,YAAY,EAAE;YACP,KAAK;IACZ,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;GAG5D,IAAI,KAAK;IACP;IACA,aAAa,KAAK,eAAe;IACjC;IACA;IACA;IACA;IACA;IACA,GAAI,UAAU,KAAA,IAAY,EAAE,OAAO,GAAG,EAAE;IACxC;IACA;IACA;IACA;IACA;IACD,CAAC;;EAEJ,OAAO"}
|
|
@@ -24,6 +24,22 @@ var MeshOrchestratorAddon = class extends BaseAddon {
|
|
|
24
24
|
const impl = this.resolveImpl(addonId);
|
|
25
25
|
if (impl?.leave) await impl.leave();
|
|
26
26
|
return { success: true };
|
|
27
|
+
},
|
|
28
|
+
startLoginProvider: async ({ addonId, hostname }) => {
|
|
29
|
+
const impl = this.resolveImpl(addonId);
|
|
30
|
+
if (!impl?.startLogin) throw new Error(`Mesh provider "${addonId}" does not support startLogin`);
|
|
31
|
+
return await impl.startLogin(hostname ? { hostname } : {});
|
|
32
|
+
},
|
|
33
|
+
logoutProvider: async ({ addonId }) => {
|
|
34
|
+
const impl = this.resolveImpl(addonId);
|
|
35
|
+
if (!impl?.logout) throw new Error(`Mesh provider "${addonId}" does not support logout`);
|
|
36
|
+
await impl.logout();
|
|
37
|
+
return { loggedOut: true };
|
|
38
|
+
},
|
|
39
|
+
listProviderPeers: async ({ addonId }) => {
|
|
40
|
+
const impl = this.resolveImpl(addonId);
|
|
41
|
+
if (!impl?.listPeers) return { peers: [] };
|
|
42
|
+
return await impl.listPeers();
|
|
27
43
|
}
|
|
28
44
|
};
|
|
29
45
|
this.ctx.logger.info("Mesh orchestrator initialized");
|
|
@@ -45,6 +61,11 @@ var MeshOrchestratorAddon = class extends BaseAddon {
|
|
|
45
61
|
let peerCount = 0;
|
|
46
62
|
let endpoints = [];
|
|
47
63
|
let error;
|
|
64
|
+
let tenantName = "";
|
|
65
|
+
let magicDnsSuffix = "";
|
|
66
|
+
let userLogin = null;
|
|
67
|
+
let controlPlaneUrl = "";
|
|
68
|
+
let keyExpiry = null;
|
|
48
69
|
if (impl.getStatus) try {
|
|
49
70
|
const s = await impl.getStatus();
|
|
50
71
|
joined = s.joined;
|
|
@@ -53,6 +74,11 @@ var MeshOrchestratorAddon = class extends BaseAddon {
|
|
|
53
74
|
peerCount = s.peerCount;
|
|
54
75
|
endpoints = s.endpoints;
|
|
55
76
|
error = s.error;
|
|
77
|
+
tenantName = s.tenantName;
|
|
78
|
+
magicDnsSuffix = s.magicDnsSuffix;
|
|
79
|
+
userLogin = s.userLogin;
|
|
80
|
+
controlPlaneUrl = s.controlPlaneUrl;
|
|
81
|
+
keyExpiry = s.keyExpiry;
|
|
56
82
|
} catch (err) {
|
|
57
83
|
error = err instanceof Error ? err.message : String(err);
|
|
58
84
|
}
|
|
@@ -64,7 +90,12 @@ var MeshOrchestratorAddon = class extends BaseAddon {
|
|
|
64
90
|
magicDnsHostname,
|
|
65
91
|
peerCount,
|
|
66
92
|
endpoints,
|
|
67
|
-
...error !== void 0 ? { error } : {}
|
|
93
|
+
...error !== void 0 ? { error } : {},
|
|
94
|
+
tenantName,
|
|
95
|
+
magicDnsSuffix,
|
|
96
|
+
userLogin,
|
|
97
|
+
controlPlaneUrl,
|
|
98
|
+
keyExpiry
|
|
68
99
|
});
|
|
69
100
|
}
|
|
70
101
|
return out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mesh-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"sourcesContent":["/**\n * Mesh orchestrator — singleton facade over the `mesh-network`\n * collection. One row per provider (Tailscale, Headscale, ZeroTier),\n * aggregated for the admin UI's Mesh Networks page.\n */\nimport {\n BaseAddon,\n meshOrchestratorCapability,\n type IMeshOrchestrator,\n type MeshProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface
|
|
1
|
+
{"version":3,"file":"mesh-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/mesh-orchestrator/mesh-orchestrator.addon.ts"],"sourcesContent":["/**\n * Mesh orchestrator — singleton facade over the `mesh-network`\n * collection. One row per provider (Tailscale, Headscale, ZeroTier),\n * aggregated for the admin UI's Mesh Networks page.\n */\nimport {\n BaseAddon,\n meshOrchestratorCapability,\n type IMeshOrchestrator,\n type MeshPeer,\n type MeshProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface MeshStatusLike {\n joined: boolean\n meshIp: string\n magicDnsHostname: string\n peerCount: number\n endpoints: readonly {\n id: string\n label: string\n scope: 'mesh' | 'public'\n url: string\n hostname: string\n port: number\n protocol: 'http' | 'https'\n }[]\n error?: string\n tenantName: string\n magicDnsSuffix: string\n userLogin: string | null\n controlPlaneUrl: string\n keyExpiry: number | null\n}\n\ninterface MeshNetworkLike {\n getStatus?: () => Promise<MeshStatusLike>\n join?: (input: { authKey: string; hostname?: string }) => Promise<{ joined: true }>\n startLogin?: (input: { hostname?: string }) => Promise<{ loginUrl: string }>\n leave?: () => Promise<{ left: true }>\n logout?: () => Promise<{ loggedOut: true }>\n listPeers?: () => Promise<{ peers: readonly MeshPeer[] }>\n}\n\ninterface MeshRegistrationMeta {\n readonly displayName?: string\n}\n\nexport class MeshOrchestratorAddon extends BaseAddon<Record<string, never>> {\n constructor() {\n super({})\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IMeshOrchestrator = {\n listProviders: async () => this.listProviders(),\n joinProvider: async ({ addonId, authKey, hostname }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.join) throw new Error(`Mesh provider \"${addonId}\" does not support join`)\n return await impl.join({ authKey, ...(hostname ? { hostname } : {}) })\n },\n leaveProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.leave) await impl.leave()\n return { success: true as const }\n },\n startLoginProvider: async ({ addonId, hostname }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.startLogin) {\n throw new Error(`Mesh provider \"${addonId}\" does not support startLogin`)\n }\n return await impl.startLogin(hostname ? { hostname } : {})\n },\n logoutProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.logout) {\n throw new Error(`Mesh provider \"${addonId}\" does not support logout`)\n }\n await impl.logout()\n return { loggedOut: true as const }\n },\n listProviderPeers: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.listPeers) return { peers: [] }\n return await impl.listPeers()\n },\n }\n this.ctx.logger.info('Mesh orchestrator initialized')\n return [{ capability: meshOrchestratorCapability, provider }]\n }\n\n private resolveImpl(addonId: string): MeshNetworkLike | null {\n const entries = this.capabilities?.getCollectionEntries<MeshNetworkLike>('mesh-network') ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly MeshProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<MeshNetworkLike & MeshRegistrationMeta>(\n 'mesh-network',\n ) ?? []\n const out: MeshProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let joined = false\n let meshIp = ''\n let magicDnsHostname = ''\n let peerCount = 0\n let endpoints: MeshProviderInfo['endpoints'] = []\n let error: string | undefined\n let tenantName = ''\n let magicDnsSuffix = ''\n let userLogin: string | null = null\n let controlPlaneUrl = ''\n let keyExpiry: number | null = null\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n joined = s.joined\n meshIp = s.meshIp\n magicDnsHostname = s.magicDnsHostname\n peerCount = s.peerCount\n endpoints = s.endpoints\n error = s.error\n tenantName = s.tenantName\n magicDnsSuffix = s.magicDnsSuffix\n userLogin = s.userLogin\n controlPlaneUrl = s.controlPlaneUrl\n keyExpiry = s.keyExpiry\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n joined,\n meshIp,\n magicDnsHostname,\n peerCount,\n endpoints,\n ...(error !== undefined ? { error } : {}),\n tenantName,\n magicDnsSuffix,\n userLogin,\n controlPlaneUrl,\n keyExpiry,\n })\n }\n return out\n }\n}\n\nexport default MeshOrchestratorAddon\n"],"mappings":";;;;;;;AAiDA,IAAa,wBAAb,cAA2C,UAAiC;CAC1E,cAAc;EACZ,MAAM,EAAE,CAAC;;CAGX,MAAgB,eAAgD;EAC9D,MAAM,WAA8B;GAClC,eAAe,YAAY,KAAK,eAAe;GAC/C,cAAc,OAAO,EAAE,SAAS,SAAS,eAAe;IACtD,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,MAAM,MAAM,IAAI,MAAM,kBAAkB,QAAQ,yBAAyB;IACpF,OAAO,MAAM,KAAK,KAAK;KAAE;KAAS,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;KAAG,CAAC;;GAExE,eAAe,OAAO,EAAE,cAAc;IACpC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,MAAM,OAAO,MAAM,KAAK,OAAO;IACnC,OAAO,EAAE,SAAS,MAAe;;GAEnC,oBAAoB,OAAO,EAAE,SAAS,eAAe;IACnD,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,YACT,MAAM,IAAI,MAAM,kBAAkB,QAAQ,+BAA+B;IAE3E,OAAO,MAAM,KAAK,WAAW,WAAW,EAAE,UAAU,GAAG,EAAE,CAAC;;GAE5D,gBAAgB,OAAO,EAAE,cAAc;IACrC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,QACT,MAAM,IAAI,MAAM,kBAAkB,QAAQ,2BAA2B;IAEvE,MAAM,KAAK,QAAQ;IACnB,OAAO,EAAE,WAAW,MAAe;;GAErC,mBAAmB,OAAO,EAAE,cAAc;IACxC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,WAAW,OAAO,EAAE,OAAO,EAAE,EAAE;IAC1C,OAAO,MAAM,KAAK,WAAW;;GAEhC;EACD,KAAK,IAAI,OAAO,KAAK,gCAAgC;EACrD,OAAO,CAAC;GAAE,YAAY;GAA4B;GAAU,CAAC;;CAG/D,YAAoB,SAAyC;EAG3D,QAFgB,KAAK,cAAc,qBAAsC,eAAe,IAAI,EAAE,EACxE,MAAM,CAAC,QAAQ,OAAO,QACrC,GAAQ,MAAM;;CAGvB,MAAc,gBAAsD;EAClE,MAAM,UAAU,KAAK,cAAc,qBACjC,eACD,IAAI,EAAE;EACP,MAAM,MAA0B,EAAE;EAClC,KAAK,MAAM,CAAC,SAAS,SAAS,SAAS;GACrC,IAAI,SAAS;GACb,IAAI,SAAS;GACb,IAAI,mBAAmB;GACvB,IAAI,YAAY;GAChB,IAAI,YAA2C,EAAE;GACjD,IAAI;GACJ,IAAI,aAAa;GACjB,IAAI,iBAAiB;GACrB,IAAI,YAA2B;GAC/B,IAAI,kBAAkB;GACtB,IAAI,YAA2B;GAC/B,IAAI,KAAK,WACP,IAAI;IACF,MAAM,IAAI,MAAM,KAAK,WAAW;IAChC,SAAS,EAAE;IACX,SAAS,EAAE;IACX,mBAAmB,EAAE;IACrB,YAAY,EAAE;IACd,YAAY,EAAE;IACd,QAAQ,EAAE;IACV,aAAa,EAAE;IACf,iBAAiB,EAAE;IACnB,YAAY,EAAE;IACd,kBAAkB,EAAE;IACpB,YAAY,EAAE;YACP,KAAK;IACZ,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;GAG5D,IAAI,KAAK;IACP;IACA,aAAa,KAAK,eAAe;IACjC;IACA;IACA;IACA;IACA;IACA,GAAI,UAAU,KAAA,IAAY,EAAE,OAAO,GAAG,EAAE;IACxC;IACA;IACA;IACA;IACA;IACD,CAAC;;EAEJ,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-access-orchestrator.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,SAAS,EAIT,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAA;AAgBxB,UAAU,8BAA8B;IACtC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7C;AAED,qBAAa,6BAA8B,SAAQ,SAAS,CAAC,8BAA8B,CAAC;;cAK1E,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"remote-access-orchestrator.addon.d.ts","sourceRoot":"","sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,SAAS,EAIT,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAA;AAgBxB,UAAU,8BAA8B;IACtC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7C;AAED,qBAAa,6BAA8B,SAAQ,SAAS,CAAC,8BAA8B,CAAC;;cAK1E,YAAY,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAyDjD,yBAAyB;YA8CzB,WAAW;IAQzB,OAAO,CAAC,WAAW;YAQL,aAAa;CAkC5B;AAED,eAAe,6BAA6B,CAAA"}
|
|
@@ -43,6 +43,12 @@ var RemoteAccessOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
43
43
|
setImmediate(() => {
|
|
44
44
|
this.autoStartEnabledProviders();
|
|
45
45
|
});
|
|
46
|
+
this.watchCapability("network-access", { onReady: () => {
|
|
47
|
+
this.autoStartEnabledProviders();
|
|
48
|
+
} });
|
|
49
|
+
this.watchCapability("mesh-network", { onReady: () => {
|
|
50
|
+
this.autoStartEnabledProviders();
|
|
51
|
+
} });
|
|
46
52
|
return [{
|
|
47
53
|
capability: _camstack_types.remoteAccessCapability,
|
|
48
54
|
provider
|
|
@@ -62,6 +68,16 @@ var RemoteAccessOrchestratorAddon = class extends _camstack_types.BaseAddon {
|
|
|
62
68
|
} });
|
|
63
69
|
continue;
|
|
64
70
|
}
|
|
71
|
+
if (impl.getStatus) {
|
|
72
|
+
const status = await impl.getStatus().catch(() => null);
|
|
73
|
+
if (status?.connected) {
|
|
74
|
+
this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
|
|
75
|
+
addonId,
|
|
76
|
+
url: status.endpoint?.url
|
|
77
|
+
} });
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
65
81
|
const endpoint = await impl.start();
|
|
66
82
|
this.ctx.logger.info("autostart: provider started", { meta: {
|
|
67
83
|
addonId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-access-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log + skip; the operator can\n // retry from the UI when the addon is ready.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remote-access-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n\n // Lazy retry — forked providers (cloudflare-tunnel etc) typically\n // register 15-20 s after this orchestrator boots, well past the\n // `setImmediate` above. Hook BaseAddon's `system.ready-state`\n // subscription so we re-run autoStart every time the\n // `network-access` cap transitions to ready (whichever node holds\n // it). The inner logic is idempotent + skips already-connected\n // providers.\n this.watchCapability('network-access', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n // Same watch for `mesh-network`. The tailscale-ingress provider\n // registers `network-access` synchronously at boot, but `start()`\n // throws when the tailnet isn't joined yet — so the boot-time\n // autoStart call fails for tailscale ingresses if the tailscale\n // daemon hadn't logged in by then. Watching `mesh-network` here\n // re-triggers autoStart the moment the client transitions to\n // joined (manual operator login or auto-rejoin), without needing\n // a server restart. Same idempotency rules: providers already\n // connected are skipped.\n this.watchCapability('mesh-network', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log at debug level — the\n // provider-registered subscription below will retry as soon\n // as the addon appears.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n // Idempotent: skip when the provider is already connected.\n // Avoids spamming start() on every provider-registered event\n // and prevents respawning a child process that's already alive.\n if (impl.getStatus) {\n const status = await impl.getStatus().catch(() => null)\n if (status?.connected) {\n this.ctx.logger.info('autostart: provider already connected — skipping', {\n meta: { addonId, url: status.endpoint?.url },\n })\n continue\n }\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
|
|
@@ -38,6 +38,12 @@ var RemoteAccessOrchestratorAddon = class extends BaseAddon {
|
|
|
38
38
|
setImmediate(() => {
|
|
39
39
|
this.autoStartEnabledProviders();
|
|
40
40
|
});
|
|
41
|
+
this.watchCapability("network-access", { onReady: () => {
|
|
42
|
+
this.autoStartEnabledProviders();
|
|
43
|
+
} });
|
|
44
|
+
this.watchCapability("mesh-network", { onReady: () => {
|
|
45
|
+
this.autoStartEnabledProviders();
|
|
46
|
+
} });
|
|
41
47
|
return [{
|
|
42
48
|
capability: remoteAccessCapability,
|
|
43
49
|
provider
|
|
@@ -57,6 +63,16 @@ var RemoteAccessOrchestratorAddon = class extends BaseAddon {
|
|
|
57
63
|
} });
|
|
58
64
|
continue;
|
|
59
65
|
}
|
|
66
|
+
if (impl.getStatus) {
|
|
67
|
+
const status = await impl.getStatus().catch(() => null);
|
|
68
|
+
if (status?.connected) {
|
|
69
|
+
this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
|
|
70
|
+
addonId,
|
|
71
|
+
url: status.endpoint?.url
|
|
72
|
+
} });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
60
76
|
const endpoint = await impl.start();
|
|
61
77
|
this.ctx.logger.info("autostart: provider started", { meta: {
|
|
62
78
|
addonId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-access-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log + skip; the operator can\n // retry from the UI when the addon is ready.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remote-access-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n\n // Lazy retry — forked providers (cloudflare-tunnel etc) typically\n // register 15-20 s after this orchestrator boots, well past the\n // `setImmediate` above. Hook BaseAddon's `system.ready-state`\n // subscription so we re-run autoStart every time the\n // `network-access` cap transitions to ready (whichever node holds\n // it). The inner logic is idempotent + skips already-connected\n // providers.\n this.watchCapability('network-access', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n // Same watch for `mesh-network`. The tailscale-ingress provider\n // registers `network-access` synchronously at boot, but `start()`\n // throws when the tailnet isn't joined yet — so the boot-time\n // autoStart call fails for tailscale ingresses if the tailscale\n // daemon hadn't logged in by then. Watching `mesh-network` here\n // re-triggers autoStart the moment the client transitions to\n // joined (manual operator login or auto-rejoin), without needing\n // a server restart. Same idempotency rules: providers already\n // connected are skipped.\n this.watchCapability('mesh-network', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log at debug level — the\n // provider-registered subscription below will retry as soon\n // as the addon appears.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n // Idempotent: skip when the provider is already connected.\n // Avoids spamming start() on every provider-registered event\n // and prevents respawning a child process that's already alive.\n if (impl.getStatus) {\n const status = await impl.getStatus().catch(() => null)\n if (status?.connected) {\n this.ctx.logger.info('autostart: provider already connected — skipping', {\n meta: { addonId, url: status.endpoint?.url },\n })\n continue\n }\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ let node_vm = require("node:vm");
|
|
|
37
37
|
node_vm = require_chunk.__toESM(node_vm);
|
|
38
38
|
let node_os = require("node:os");
|
|
39
39
|
node_os = require_chunk.__toESM(node_os);
|
|
40
|
-
//#region ../types/dist/index-
|
|
40
|
+
//#region ../types/dist/index-Ce7RZWP4.mjs
|
|
41
41
|
var MODEL_FORMATS = [
|
|
42
42
|
"onnx",
|
|
43
43
|
"coreml",
|
|
@@ -2654,10 +2654,30 @@ var LinkStateSchema = zod.z.enum([
|
|
|
2654
2654
|
"linked",
|
|
2655
2655
|
"error"
|
|
2656
2656
|
]);
|
|
2657
|
+
var ExportSetupFieldSchema = zod.z.object({
|
|
2658
|
+
label: zod.z.string(),
|
|
2659
|
+
value: zod.z.string(),
|
|
2660
|
+
/** Mask the value by default + render a reveal toggle (client id, secrets). */
|
|
2661
|
+
secret: zod.z.boolean().optional()
|
|
2662
|
+
});
|
|
2663
|
+
var ExportSetupSchema = zod.z.object({
|
|
2664
|
+
/** A string to render as a scannable QR — HAP `X-HM://…` URI, a pairing URL, etc. Omitted when there's nothing to scan. */
|
|
2665
|
+
qr: zod.z.string().optional(),
|
|
2666
|
+
/** Label/value rows shown with a copy button (HAP setup code, OAuth URLs, client id, linked-account count, …). */
|
|
2667
|
+
fields: zod.z.array(ExportSetupFieldSchema).readonly().optional(),
|
|
2668
|
+
/** Free-form operator instructions rendered above the fields. */
|
|
2669
|
+
note: zod.z.string().optional()
|
|
2670
|
+
});
|
|
2657
2671
|
var DeviceExportStatusSchema = zod.z.object({
|
|
2658
2672
|
linkState: LinkStateSchema,
|
|
2659
2673
|
exposedDeviceCount: zod.z.number(),
|
|
2660
|
-
error: zod.z.string().optional()
|
|
2674
|
+
error: zod.z.string().optional(),
|
|
2675
|
+
/**
|
|
2676
|
+
* Optional pairing/account info the panel renders in a generic
|
|
2677
|
+
* "Setup" section. Addon-agnostic — the addon id identifies the
|
|
2678
|
+
* export target, never an `ecosystem` key here.
|
|
2679
|
+
*/
|
|
2680
|
+
setup: ExportSetupSchema.optional()
|
|
2661
2681
|
});
|
|
2662
2682
|
var DeviceKindSchema = zod.z.string();
|
|
2663
2683
|
var ExposedDeviceSchema = zod.z.object({
|
|
@@ -4814,7 +4834,9 @@ var MeshPeerSchema = zod.z.object({
|
|
|
4814
4834
|
id: zod.z.string(),
|
|
4815
4835
|
/** Hostname / device name as shown in the mesh dashboard. */
|
|
4816
4836
|
hostname: zod.z.string(),
|
|
4817
|
-
/**
|
|
4837
|
+
/** MagicDNS / mesh DNS name (e.g. `peer-foo.tail-abc.ts.net`). Empty when the provider doesn't support DNS or peer-side resolution is disabled. */
|
|
4838
|
+
magicDns: zod.z.string(),
|
|
4839
|
+
/** Mesh IPv4 / IPv6 addresses combined. Multiple when dual-stacked. */
|
|
4818
4840
|
addresses: zod.z.array(zod.z.string()).readonly(),
|
|
4819
4841
|
/** Operating system reported by the peer (informational). */
|
|
4820
4842
|
os: zod.z.string().optional(),
|
|
@@ -4823,7 +4845,36 @@ var MeshPeerSchema = zod.z.object({
|
|
|
4823
4845
|
/** Last-seen timestamp (epoch ms). 0 when never seen. */
|
|
4824
4846
|
lastSeenMs: zod.z.number(),
|
|
4825
4847
|
/** True for the row representing the current host. */
|
|
4826
|
-
isSelf: zod.z.boolean()
|
|
4848
|
+
isSelf: zod.z.boolean(),
|
|
4849
|
+
/**
|
|
4850
|
+
* Connection mode to this peer:
|
|
4851
|
+
* - `direct`: NAT-traversed P2P (UDP punched)
|
|
4852
|
+
* - `relay`: traffic relayed through provider edge (Tailscale DERP)
|
|
4853
|
+
* - `idle`: peer reachable but no recent traffic / handshake
|
|
4854
|
+
*/
|
|
4855
|
+
connection: zod.z.enum([
|
|
4856
|
+
"direct",
|
|
4857
|
+
"relay",
|
|
4858
|
+
"idle"
|
|
4859
|
+
]),
|
|
4860
|
+
/** DERP region / relay node identifier when `connection: relay`. Null otherwise. */
|
|
4861
|
+
relay: zod.z.string().nullable(),
|
|
4862
|
+
/** Last successful handshake timestamp (epoch ms). 0 when never. */
|
|
4863
|
+
lastHandshakeMs: zod.z.number(),
|
|
4864
|
+
/** Bytes received from this peer since the daemon started. */
|
|
4865
|
+
rxBytes: zod.z.number(),
|
|
4866
|
+
/** Bytes transmitted to this peer since the daemon started. */
|
|
4867
|
+
txBytes: zod.z.number(),
|
|
4868
|
+
/** ACL tags this peer carries (e.g. `["tag:server"]`). Empty when human-owned. */
|
|
4869
|
+
tags: zod.z.array(zod.z.string()).readonly(),
|
|
4870
|
+
/** True when the peer advertises itself as an exit-node candidate. */
|
|
4871
|
+
exitNodeOption: zod.z.boolean(),
|
|
4872
|
+
/** Subnet routes the peer advertises (e.g. `["192.168.1.0/24"]`). */
|
|
4873
|
+
advertisedRoutes: zod.z.array(zod.z.string()).readonly(),
|
|
4874
|
+
/** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
|
|
4875
|
+
userLogin: zod.z.string().nullable(),
|
|
4876
|
+
/** Pre-auth key / machine-key expiry (epoch ms). Null when keys don't expire. */
|
|
4877
|
+
keyExpiry: zod.z.number().nullable()
|
|
4827
4878
|
});
|
|
4828
4879
|
var MeshStatusSchema = zod.z.object({
|
|
4829
4880
|
/** True when the host is joined and the daemon reports healthy. */
|
|
@@ -4837,7 +4888,54 @@ var MeshStatusSchema = zod.z.object({
|
|
|
4837
4888
|
/** Every endpoint this provider exposes for the current host. */
|
|
4838
4889
|
endpoints: zod.z.array(MeshEndpointSchema$1).readonly(),
|
|
4839
4890
|
/** Last error from the daemon, when not joined. */
|
|
4840
|
-
error: zod.z.string().optional()
|
|
4891
|
+
error: zod.z.string().optional(),
|
|
4892
|
+
/**
|
|
4893
|
+
* Tenant / tailnet / network display name (Tailscale = tailnet
|
|
4894
|
+
* domain, Headscale = namespace, ZeroTier = network name). Empty
|
|
4895
|
+
* pre-join. Per-provider semantics, common slot.
|
|
4896
|
+
*/
|
|
4897
|
+
tenantName: zod.z.string(),
|
|
4898
|
+
/**
|
|
4899
|
+
* Mesh DNS suffix when the provider exposes peer-resolvable DNS
|
|
4900
|
+
* (Tailscale MagicDNS, Headscale MagicDNS, …). Empty when the
|
|
4901
|
+
* provider doesn't ship DNS (e.g. ZeroTier).
|
|
4902
|
+
*/
|
|
4903
|
+
magicDnsSuffix: zod.z.string(),
|
|
4904
|
+
/**
|
|
4905
|
+
* Authenticated user / account login bound to this host. Null when
|
|
4906
|
+
* the provider authenticates with a long-lived network token rather
|
|
4907
|
+
* than a user account (e.g. ZeroTier API token).
|
|
4908
|
+
*/
|
|
4909
|
+
userLogin: zod.z.string().nullable(),
|
|
4910
|
+
/**
|
|
4911
|
+
* Provider control-plane URL (Tailscale SaaS `https://login.tailscale.com`,
|
|
4912
|
+
* self-hosted Headscale, ZeroTier Central, …). Empty when not
|
|
4913
|
+
* applicable (rare).
|
|
4914
|
+
*/
|
|
4915
|
+
controlPlaneUrl: zod.z.string(),
|
|
4916
|
+
/**
|
|
4917
|
+
* Machine-key / node-key expiry (epoch ms). Null when the provider
|
|
4918
|
+
* doesn't rotate keys for the bound host. Operator-facing surface
|
|
4919
|
+
* for "your access expires on …" banners.
|
|
4920
|
+
*/
|
|
4921
|
+
keyExpiry: zod.z.number().nullable(),
|
|
4922
|
+
/**
|
|
4923
|
+
* When the provider runs its OWN mesh daemon (e.g. the Tailscale
|
|
4924
|
+
* client addon in `onboard` mode spawns a private `tailscaled`),
|
|
4925
|
+
* this carries the local control-socket path. Companion addons that
|
|
4926
|
+
* must drive the SAME daemon — chiefly `tailscale-ingress` for
|
|
4927
|
+
* Serve/Funnel — read it to point their CLI at the right socket
|
|
4928
|
+
* instead of the system default. Empty when the provider uses the
|
|
4929
|
+
* host's system daemon (or doesn't have the concept).
|
|
4930
|
+
*/
|
|
4931
|
+
daemonSocket: zod.z.string().optional(),
|
|
4932
|
+
/**
|
|
4933
|
+
* Path to the mesh CLI binary the provider downloaded for onboard
|
|
4934
|
+
* mode. Companion addons reuse it so they don't need a system
|
|
4935
|
+
* install when the operator chose a fully self-contained mesh.
|
|
4936
|
+
* Empty in host mode.
|
|
4937
|
+
*/
|
|
4938
|
+
daemonCliPath: zod.z.string().optional()
|
|
4841
4939
|
});
|
|
4842
4940
|
method(zod.z.void(), MeshStatusSchema), method(zod.z.object({
|
|
4843
4941
|
/** Provider-specific auth key. For Tailscale this is the
|
|
@@ -4849,7 +4947,7 @@ method(zod.z.void(), MeshStatusSchema), method(zod.z.object({
|
|
|
4849
4947
|
/** Optional hostname override the host should advertise once joined. */
|
|
4850
4948
|
hostname: zod.z.string().optional() }), zod.z.object({
|
|
4851
4949
|
/** Authentication URL the operator should open in a browser. */
|
|
4852
|
-
loginUrl: zod.z.string() }), { kind: "mutation" }), method(zod.z.void(), zod.z.object({ left: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.void(), zod.z.object({ peers: zod.z.array(MeshPeerSchema).readonly() })), method(zod.z.object({
|
|
4950
|
+
loginUrl: zod.z.string() }), { kind: "mutation" }), method(zod.z.void(), zod.z.object({ left: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.void(), zod.z.object({ loggedOut: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.void(), zod.z.object({ peers: zod.z.array(MeshPeerSchema).readonly() })), method(zod.z.object({
|
|
4853
4951
|
/** Optional auth key — when provided, probes the key validity
|
|
4854
4952
|
* against the provider's API. Omit when already joined to
|
|
4855
4953
|
* just ping the daemon. */
|
|
@@ -4888,13 +4986,26 @@ var MeshProviderInfoSchema = zod.z.object({
|
|
|
4888
4986
|
/** Active endpoints (mesh IP + MagicDNS + optional public Funnel). */
|
|
4889
4987
|
endpoints: zod.z.array(MeshEndpointSchema).readonly(),
|
|
4890
4988
|
/** Last error reported by the provider. */
|
|
4891
|
-
error: zod.z.string().optional()
|
|
4989
|
+
error: zod.z.string().optional(),
|
|
4990
|
+
/** Tenant / tailnet / network display name. Empty pre-join. */
|
|
4991
|
+
tenantName: zod.z.string(),
|
|
4992
|
+
/** Mesh DNS suffix (e.g. tailXXXX.ts.net). Empty when not configured. */
|
|
4993
|
+
magicDnsSuffix: zod.z.string(),
|
|
4994
|
+
/** Authenticated user / account login. Null for token-only providers. */
|
|
4995
|
+
userLogin: zod.z.string().nullable(),
|
|
4996
|
+
/** Provider control-plane URL. */
|
|
4997
|
+
controlPlaneUrl: zod.z.string(),
|
|
4998
|
+
/** Machine-key expiry (epoch ms). Null when keys don't rotate. */
|
|
4999
|
+
keyExpiry: zod.z.number().nullable()
|
|
4892
5000
|
});
|
|
4893
5001
|
method(zod.z.void(), zod.z.array(MeshProviderInfoSchema).readonly()), method(zod.z.object({
|
|
4894
5002
|
addonId: zod.z.string(),
|
|
4895
5003
|
authKey: zod.z.string().min(8),
|
|
4896
5004
|
hostname: zod.z.string().optional()
|
|
4897
|
-
}), zod.z.object({ joined: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" })
|
|
5005
|
+
}), zod.z.object({ joined: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.object({
|
|
5006
|
+
addonId: zod.z.string(),
|
|
5007
|
+
hostname: zod.z.string().optional()
|
|
5008
|
+
}), zod.z.object({ loginUrl: zod.z.string() }), { kind: "mutation" }), method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ loggedOut: zod.z.literal(true) }), { kind: "mutation" }), method(zod.z.object({ addonId: zod.z.string() }), zod.z.object({ peers: zod.z.array(MeshPeerSchema).readonly() }));
|
|
4898
5009
|
var MethodAccessSchema = zod.z.enum([
|
|
4899
5010
|
"view",
|
|
4900
5011
|
"create",
|
|
@@ -4981,6 +5092,13 @@ var UserSummarySchema = zod.z.object({
|
|
|
4981
5092
|
allowedProviders: zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")]),
|
|
4982
5093
|
allowedDevices: zod.z.record(zod.z.string(), zod.z.union([zod.z.array(zod.z.string()), zod.z.literal("*")])),
|
|
4983
5094
|
scopes: zod.z.array(TokenScopeSchema).default([]),
|
|
5095
|
+
/**
|
|
5096
|
+
* True iff this user has a CONFIRMED TOTP enrollment. Surfaced on
|
|
5097
|
+
* `listUsers` so the admin Users page can render the "Remove 2FA"
|
|
5098
|
+
* action conditionally without firing one `getTotpStatus` query per
|
|
5099
|
+
* row. Self-service enrollment lives on the user's profile page.
|
|
5100
|
+
*/
|
|
5101
|
+
totpEnabled: zod.z.boolean().default(false),
|
|
4984
5102
|
createdAt: zod.z.number(),
|
|
4985
5103
|
updatedAt: zod.z.number()
|
|
4986
5104
|
});
|
|
@@ -5243,6 +5361,12 @@ var NodeAddonInputSchema = zod.z.object({
|
|
|
5243
5361
|
nodeId: zod.z.string(),
|
|
5244
5362
|
addonId: zod.z.string()
|
|
5245
5363
|
});
|
|
5364
|
+
var NodeAddonEntrySchema = zod.z.object({
|
|
5365
|
+
id: zod.z.string(),
|
|
5366
|
+
status: zod.z.string(),
|
|
5367
|
+
version: zod.z.string().optional(),
|
|
5368
|
+
packageName: zod.z.string().optional()
|
|
5369
|
+
});
|
|
5246
5370
|
var SuccessSchema = zod.z.object({ success: zod.z.boolean() });
|
|
5247
5371
|
var RestartProcessResultSchema = zod.z.object({
|
|
5248
5372
|
success: zod.z.boolean(),
|
|
@@ -5283,7 +5407,7 @@ method(zod.z.void(), zod.z.array(TopologyNodeSchema).readonly(), { auth: "admin"
|
|
|
5283
5407
|
}), RenameNodeResultSchema, {
|
|
5284
5408
|
kind: "mutation",
|
|
5285
5409
|
auth: "admin"
|
|
5286
|
-
}), method(zod.z.void(), zod.z.record(zod.z.string(), ClusterAddonStatusEntrySchema), { auth: "admin" }), method(zod.z.object({
|
|
5410
|
+
}), method(zod.z.void(), zod.z.record(zod.z.string(), ClusterAddonStatusEntrySchema), { auth: "admin" }), method(zod.z.object({ nodeId: zod.z.string() }), zod.z.array(NodeAddonEntrySchema).readonly(), { auth: "admin" }), method(zod.z.object({
|
|
5287
5411
|
nodeId: zod.z.string(),
|
|
5288
5412
|
level: zod.z.string()
|
|
5289
5413
|
}), SuccessSchema, {
|
|
@@ -5493,6 +5617,21 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
|
5493
5617
|
var RestartAddonResultSchema = zod.z.unknown();
|
|
5494
5618
|
var InstallPackageResultSchema = zod.z.unknown();
|
|
5495
5619
|
var ReloadPackagesResultSchema = zod.z.unknown();
|
|
5620
|
+
var UpdateFrameworkPackageResultSchema = zod.z.object({
|
|
5621
|
+
packageName: zod.z.string(),
|
|
5622
|
+
fromVersion: zod.z.string(),
|
|
5623
|
+
toVersion: zod.z.string(),
|
|
5624
|
+
/** Ms-epoch the server scheduled its self-restart. */
|
|
5625
|
+
restartingAt: zod.z.number()
|
|
5626
|
+
});
|
|
5627
|
+
var FrameworkPackageStatusSchema = zod.z.object({
|
|
5628
|
+
packageName: zod.z.string(),
|
|
5629
|
+
currentVersion: zod.z.string(),
|
|
5630
|
+
latestVersion: zod.z.string().nullable(),
|
|
5631
|
+
hasUpdate: zod.z.boolean(),
|
|
5632
|
+
/** Optional manifest description for the row tooltip. */
|
|
5633
|
+
description: zod.z.string().optional()
|
|
5634
|
+
});
|
|
5496
5635
|
var LogStreamEntrySchema = zod.z.object({
|
|
5497
5636
|
timestamp: zod.z.string(),
|
|
5498
5637
|
level: zod.z.string(),
|
|
@@ -5524,21 +5663,43 @@ method(zod.z.void(), zod.z.array(AddonListItemSchema).readonly()), method(zod.z.
|
|
|
5524
5663
|
}), method(zod.z.void(), ReloadPackagesResultSchema, {
|
|
5525
5664
|
kind: "mutation",
|
|
5526
5665
|
auth: "admin"
|
|
5527
|
-
}), method(zod.z.object({ query: zod.z.string().optional() }), zod.z.array(SearchResultSchema)), method(zod.z.
|
|
5666
|
+
}), method(zod.z.object({ query: zod.z.string().optional() }), zod.z.array(SearchResultSchema)), method(zod.z.object({ nodeId: zod.z.string().optional() }), zod.z.array(PackageUpdateSchema).readonly(), { auth: "admin" }), method(zod.z.object({
|
|
5528
5667
|
name: zod.z.string().min(1),
|
|
5529
|
-
version: zod.z.string().optional()
|
|
5668
|
+
version: zod.z.string().optional(),
|
|
5669
|
+
nodeId: zod.z.string().optional()
|
|
5530
5670
|
}), zod.z.unknown(), {
|
|
5531
5671
|
kind: "mutation",
|
|
5532
5672
|
auth: "admin"
|
|
5533
5673
|
}), method(zod.z.object({ name: zod.z.string().min(1) }), zod.z.object({ rolledBackTo: zod.z.string().nullable() }), {
|
|
5534
5674
|
kind: "mutation",
|
|
5535
5675
|
auth: "admin"
|
|
5536
|
-
}), method(zod.z.
|
|
5676
|
+
}), method(zod.z.object({ nodeId: zod.z.string().optional() }), zod.z.unknown(), {
|
|
5537
5677
|
kind: "mutation",
|
|
5538
5678
|
auth: "admin"
|
|
5539
5679
|
}), method(zod.z.object({ confirm: zod.z.literal(true) }), zod.z.unknown(), {
|
|
5540
5680
|
kind: "mutation",
|
|
5541
5681
|
auth: "admin"
|
|
5682
|
+
}), method(zod.z.void(), zod.z.object({
|
|
5683
|
+
kind: zod.z.enum([
|
|
5684
|
+
"framework-update",
|
|
5685
|
+
"manual",
|
|
5686
|
+
"system"
|
|
5687
|
+
]),
|
|
5688
|
+
packageName: zod.z.string().optional(),
|
|
5689
|
+
fromVersion: zod.z.string().optional(),
|
|
5690
|
+
toVersion: zod.z.string().optional(),
|
|
5691
|
+
requestedBy: zod.z.string().optional(),
|
|
5692
|
+
requestedAt: zod.z.number()
|
|
5693
|
+
}).nullable(), { auth: "admin" }), method(zod.z.void(), zod.z.array(FrameworkPackageStatusSchema).readonly(), { auth: "admin" }), method(zod.z.object({ capName: zod.z.string().min(1) }), zod.z.array(zod.z.object({
|
|
5694
|
+
addonId: zod.z.string(),
|
|
5695
|
+
mode: zod.z.enum(["singleton", "collection"]),
|
|
5696
|
+
isActive: zod.z.boolean()
|
|
5697
|
+
})).readonly()), method(zod.z.object({
|
|
5698
|
+
packageName: zod.z.string().min(1),
|
|
5699
|
+
version: zod.z.string().optional()
|
|
5700
|
+
}), UpdateFrameworkPackageResultSchema, {
|
|
5701
|
+
kind: "mutation",
|
|
5702
|
+
auth: "admin"
|
|
5542
5703
|
}), method(zod.z.object({ name: zod.z.string() }), zod.z.array(PackageVersionInfoSchema).readonly()), method(zod.z.object({ addonId: zod.z.string() }), RestartAddonResultSchema, {
|
|
5543
5704
|
kind: "mutation",
|
|
5544
5705
|
auth: "admin"
|