@capybara-hq/kernel 0.1.0-rc.19 → 0.1.0-rc.20
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/bootstrap.json +25 -25
- package/dist/admin/admin-agents.controller.d.ts +28 -0
- package/dist/admin/admin-agents.controller.js +79 -0
- package/dist/admin/admin-connectors.controller.d.ts +29 -0
- package/dist/admin/admin-connectors.controller.js +79 -0
- package/dist/admin/admin-items.schemas.d.ts +42 -0
- package/dist/admin/admin-items.schemas.js +48 -0
- package/dist/admin/admin.controller.d.ts +11 -20
- package/dist/admin/admin.controller.js +28 -34
- package/dist/admin/admin.module.d.ts +4 -1
- package/dist/admin/admin.module.js +50 -4
- package/dist/admin/admin.schemas.d.ts +4 -0
- package/dist/admin/admin.schemas.js +4 -1
- package/dist/admin/audit.controller.d.ts +14 -0
- package/dist/admin/audit.controller.js +42 -0
- package/dist/admin/audit.schemas.d.ts +8 -0
- package/dist/admin/audit.schemas.js +17 -0
- package/dist/admin/health.service.d.ts +104 -0
- package/dist/admin/health.service.js +246 -0
- package/dist/admin/overview.controller.d.ts +31 -0
- package/dist/admin/overview.controller.js +78 -0
- package/dist/admin/overview.schemas.d.ts +8 -0
- package/dist/admin/overview.schemas.js +13 -0
- package/dist/admin/overview.service.d.ts +112 -0
- package/dist/admin/overview.service.js +223 -0
- package/dist/admin/platform-audit.service.d.ts +41 -0
- package/dist/admin/platform-audit.service.js +70 -0
- package/dist/admin/usage.service.d.ts +149 -0
- package/dist/admin/usage.service.js +321 -0
- package/dist/agents/admin-agents.d.ts +92 -0
- package/dist/agents/admin-agents.js +170 -0
- package/dist/agents/agents.module.d.ts +4 -3
- package/dist/agents/agents.module.js +7 -3
- package/dist/agents/agents.service.d.ts +41 -3
- package/dist/agents/agents.service.js +113 -27
- package/dist/agents/definition.d.ts +16 -0
- package/dist/agents/definition.js +32 -2
- package/dist/agents/org-templates.js +4 -0
- package/dist/background/launch.d.ts +1 -1
- package/dist/background/launch.js +3 -3
- package/dist/background/tasks.service.d.ts +35 -3
- package/dist/background/tasks.service.js +91 -36
- package/dist/catalog/catalog.module.js +8 -2
- package/dist/channels/channels.module.js +11 -2
- package/dist/channels/supervisor.d.ts +2 -0
- package/dist/channels/supervisor.js +4 -0
- package/dist/conversations/events.js +1 -1
- package/dist/conversations/titles.js +4 -8
- package/dist/db/kernel-schema.d.ts +8 -1
- package/dist/devices/devices.module.js +5 -2
- package/dist/errors.d.ts +22 -2
- package/dist/errors.js +22 -2
- package/dist/evals/evals.module.js +11 -2
- package/dist/extensions/permission-view.js +3 -3
- package/dist/http/api.module.js +10 -1
- package/dist/identity/settings.js +3 -2
- package/dist/identity/write-actor.d.ts +11 -0
- package/dist/identity/write-actor.js +7 -0
- package/dist/inbox/inbox.service.d.ts +2 -1
- package/dist/inbox/platform-inbox.service.d.ts +7 -1
- package/dist/inbox/platform-inbox.service.js +20 -2
- package/dist/infra/presence/presence.module.d.ts +6 -0
- package/dist/infra/presence/presence.module.js +22 -0
- package/dist/infra/presence/replica-presence.d.ts +87 -0
- package/dist/infra/presence/replica-presence.js +189 -0
- package/dist/kernel.module.js +3 -0
- package/dist/lifecycle/kernel-lifecycle.d.ts +3 -1
- package/dist/lifecycle/kernel-lifecycle.js +17 -3
- package/dist/local-devices/local-device-gateway.d.ts +6 -0
- package/dist/local-devices/local-device-gateway.js +12 -0
- package/dist/overrides.d.ts +7 -1
- package/dist/overrides.js +4 -0
- package/dist/platform/exclusions.js +15 -0
- package/dist/platform/operation.d.ts +1 -1
- package/dist/platform/operation.js +1 -1
- package/dist/platform/ops/admin-items.ops.d.ts +37 -0
- package/dist/platform/ops/admin-items.ops.js +278 -0
- package/dist/platform/ops/admin.ops.d.ts +10 -10
- package/dist/platform/ops/admin.ops.js +111 -42
- package/dist/platform/ops/agent-skills.ops.d.ts +29 -3
- package/dist/platform/ops/agent-skills.ops.js +290 -7
- package/dist/platform/ops/inbox.ops.js +7 -4
- package/dist/platform/ops/platform.ops.js +1 -1
- package/dist/platform/ops/skills.ops.js +1 -57
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +8 -0
- package/dist/roles/gateway-role.d.ts +4 -0
- package/dist/roles/gateway-role.js +8 -0
- package/dist/runtime/run-jobs.js +4 -2
- package/dist/sandbox/sandbox-file-reader.d.ts +35 -0
- package/dist/sandbox/sandbox-file-reader.js +98 -0
- package/dist/skills/agent-skills.controller.d.ts +48 -0
- package/dist/skills/agent-skills.controller.js +131 -0
- package/dist/skills/agent-skills.service.d.ts +262 -0
- package/dist/skills/agent-skills.service.js +996 -0
- package/dist/skills/catalog.d.ts +8 -6
- package/dist/skills/catalog.js +8 -4
- package/dist/skills/changes.controller.d.ts +5 -22
- package/dist/skills/changes.controller.js +7 -55
- package/dist/skills/changes.service.d.ts +69 -115
- package/dist/skills/changes.service.js +249 -599
- package/dist/skills/conversation-catalog.d.ts +53 -0
- package/dist/skills/conversation-catalog.js +149 -0
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/package.d.ts +0 -5
- package/dist/skills/package.js +0 -14
- package/dist/skills/skills.controller.js +1 -10
- package/dist/skills/skills.module.js +15 -2
- package/dist/skills/skills.schemas.d.ts +11 -15
- package/dist/skills/skills.schemas.js +27 -20
- package/dist/skills/skills.service.d.ts +37 -30
- package/dist/skills/skills.service.js +107 -59
- package/dist/skills/usable.d.ts +4 -3
- package/dist/skills/usable.js +13 -4
- package/dist/tools/admin-connectors.d.ts +69 -0
- package/dist/tools/admin-connectors.js +135 -0
- package/dist/tools/builtin-names.d.ts +9 -14
- package/dist/tools/builtin-names.js +13 -22
- package/dist/tools/builtins/builtin-tools.d.ts +2 -3
- package/dist/tools/builtins/builtin-tools.js +21 -26
- package/dist/tools/builtins/sandbox/bash.js +2 -2
- package/dist/tools/builtins/skill-load.js +1 -0
- package/dist/tools/connectors.controller.d.ts +5 -1
- package/dist/tools/connectors.controller.js +17 -3
- package/dist/tools/connectors.service.d.ts +20 -1
- package/dist/tools/connectors.service.js +61 -0
- package/dist/tools/legacy-keys.js +0 -1
- package/dist/tools/org-tool-switches.d.ts +3 -2
- package/dist/tools/org-tool-switches.js +4 -4
- package/dist/tools/policy.d.ts +7 -9
- package/dist/tools/policy.js +11 -22
- package/dist/tools/tools.module.js +3 -0
- package/package.json +1 -1
- package/src/migrations/0109_audit_platform_idx.sql +5 -0
- package/src/migrations/0110_usage_time_idx.sql +8 -0
- package/src/migrations/0111_agent_skill_sharing.sql +22 -0
- package/src/migrations/0112_skill_share_requests.sql +4 -0
- package/dist/tools/builtins/skill-write.d.ts +0 -59
- package/dist/tools/builtins/skill-write.js +0 -244
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Global, Module } from "@nestjs/common";
|
|
8
|
+
import { ReplicaPresence } from "./replica-presence.js";
|
|
9
|
+
/**
|
|
10
|
+
* Global: {@link ReplicaPresence}, on every role (each process reports itself; `KernelLifecycle`
|
|
11
|
+
* starts and stops it), read by the admin health overview.
|
|
12
|
+
*/
|
|
13
|
+
let PresenceModule = class PresenceModule {
|
|
14
|
+
};
|
|
15
|
+
PresenceModule = __decorate([
|
|
16
|
+
Global(),
|
|
17
|
+
Module({
|
|
18
|
+
providers: [ReplicaPresence],
|
|
19
|
+
exports: [ReplicaPresence],
|
|
20
|
+
})
|
|
21
|
+
], PresenceModule);
|
|
22
|
+
export { PresenceModule };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Redis } from "ioredis";
|
|
2
|
+
import type { KernelLogger } from "../../log.js";
|
|
3
|
+
import { KernelConfigService } from "../config/kernel-config.service.js";
|
|
4
|
+
/** How often each process writes its presence (admin overview spec §5.1). */
|
|
5
|
+
export declare const PRESENCE_DEFAULT_INTERVAL_MS = 15000;
|
|
6
|
+
/** Optional provider: the presence interval (ms) instead of {@link PRESENCE_DEFAULT_INTERVAL_MS} (tests). */
|
|
7
|
+
export declare const PRESENCE_INTERVAL_MS: unique symbol;
|
|
8
|
+
/** The ZSET of live replicas: member = replica id, score = last beat (Redis server clock, ms). */
|
|
9
|
+
export declare const REPLICAS_KEY = "capk:replicas";
|
|
10
|
+
/** One replica's facts: `capk:replica:<replicaId>`. */
|
|
11
|
+
export declare const replicaKey: (replicaId: string) => string;
|
|
12
|
+
/** A kernel role (`roles` in the config). */
|
|
13
|
+
export type ReplicaRole = "api" | "worker" | "gateway";
|
|
14
|
+
/** What a live replica last reported about itself. */
|
|
15
|
+
export interface LiveReplica {
|
|
16
|
+
/** The process id (`ReplicaStatusStore.replicaId`, the gateway heartbeat's member). */
|
|
17
|
+
id: string;
|
|
18
|
+
roles: ReplicaRole[];
|
|
19
|
+
/** `KERNEL_VERSION` of the process. */
|
|
20
|
+
version: string;
|
|
21
|
+
/** When the process's start sequence was done, before it listens (ISO 8601). */
|
|
22
|
+
startedAt: string;
|
|
23
|
+
/** The configured `http.replicaId` (the `x-capybara-replica` header), null when unset. */
|
|
24
|
+
httpReplicaId: string | null;
|
|
25
|
+
/** Channel long connections this process holds (0 without the gateway role). */
|
|
26
|
+
channelsHeld: number;
|
|
27
|
+
/** Device sockets open on this process (0 without the gateway role). */
|
|
28
|
+
devicesConnected: number;
|
|
29
|
+
}
|
|
30
|
+
/** The per-process facts the presence reports, read at every beat. */
|
|
31
|
+
export interface PresenceSources {
|
|
32
|
+
/** This process's replica id (`ReplicaStatusStore.replicaId`). */
|
|
33
|
+
replicaId: string;
|
|
34
|
+
channelsHeld(): number;
|
|
35
|
+
devicesConnected(): number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The replicas with a beat newer than `windowMs` (Redis server clock): one script for the members,
|
|
39
|
+
* then one pipelined HGETALL each. A member whose facts already expired is left out. Sorted by
|
|
40
|
+
* start time, then id.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readLiveReplicas(redis: Pick<Redis, "eval" | "pipeline">, windowMs: number): Promise<LiveReplica[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Replica presence (admin overview spec §5.1): every process, whatever its roles, says every
|
|
45
|
+
* {@link PRESENCE_DEFAULT_INTERVAL_MS} that it is alive, with its roles, version, start time and
|
|
46
|
+
* the connections it holds, so the admin console can tell which replicas run in which role.
|
|
47
|
+
*
|
|
48
|
+
* Multi-replica: the state lives only in Redis, with a TTL of three intervals on both keys; the
|
|
49
|
+
* score is the Redis server clock (replica clocks may drift), as in the gateway heartbeat. Each
|
|
50
|
+
* process writes only its own entry and holds nothing other processes need: no lock, no
|
|
51
|
+
* transaction, no shared job. So it is not a `@LockedInterval` (that runs once across replicas)
|
|
52
|
+
* but a `setTimeout` chain of this process (`unref`), started by `KernelLifecycle` at the end of
|
|
53
|
+
* the start (after the start gate opened) and stopped first thing in the stop, which removes the
|
|
54
|
+
* entry at once; a crashed process ages out of {@link live} within the window. A failing beat is
|
|
55
|
+
* logged at warn and never thrown.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ReplicaPresence {
|
|
58
|
+
private readonly redis;
|
|
59
|
+
private readonly config;
|
|
60
|
+
private readonly log;
|
|
61
|
+
/** The beat interval; the window (and the TTL) is three of them. */
|
|
62
|
+
readonly intervalMs: number;
|
|
63
|
+
private sources;
|
|
64
|
+
private startedAt;
|
|
65
|
+
private timer;
|
|
66
|
+
private running;
|
|
67
|
+
private halted;
|
|
68
|
+
constructor(redis: Redis, config: Pick<KernelConfigService, "get">, log: Pick<KernelLogger, "warn">, intervalMs?: number);
|
|
69
|
+
/** How long an entry stays live without a beat (ms). */
|
|
70
|
+
get windowMs(): number;
|
|
71
|
+
/** Starts beating (the first beat at once). Called once, at the end of the start. */
|
|
72
|
+
start(sources: PresenceSources): void;
|
|
73
|
+
private schedule;
|
|
74
|
+
private beat;
|
|
75
|
+
/**
|
|
76
|
+
* Stops beating and waits for a beat in flight, leaving the entry to age out (a crash looks like
|
|
77
|
+
* this; the tests use it as one). Idempotent.
|
|
78
|
+
*/
|
|
79
|
+
halt(): Promise<void>;
|
|
80
|
+
/** The stop: {@link halt}, then removes this process's entry at once (best effort). Idempotent. */
|
|
81
|
+
stop(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* The live replicas (a beat within the window, by the Redis server clock), sorted by start time.
|
|
84
|
+
* Throws when Redis does not answer.
|
|
85
|
+
*/
|
|
86
|
+
live(): Promise<LiveReplica[]>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Inject, Injectable, Optional } from "@nestjs/common";
|
|
11
|
+
import { KERNEL_VERSION } from "../../version.js";
|
|
12
|
+
import { KernelConfigService } from "../config/kernel-config.service.js";
|
|
13
|
+
import { KERNEL_LOGGER, REDIS } from "../tokens.js";
|
|
14
|
+
/** How often each process writes its presence (admin overview spec §5.1). */
|
|
15
|
+
export const PRESENCE_DEFAULT_INTERVAL_MS = 15_000;
|
|
16
|
+
/** Optional provider: the presence interval (ms) instead of {@link PRESENCE_DEFAULT_INTERVAL_MS} (tests). */
|
|
17
|
+
export const PRESENCE_INTERVAL_MS = Symbol("PRESENCE_INTERVAL_MS");
|
|
18
|
+
/** The ZSET of live replicas: member = replica id, score = last beat (Redis server clock, ms). */
|
|
19
|
+
export const REPLICAS_KEY = "capk:replicas";
|
|
20
|
+
/** One replica's facts: `capk:replica:<replicaId>`. */
|
|
21
|
+
export const replicaKey = (replicaId) => `capk:replica:${replicaId}`;
|
|
22
|
+
/**
|
|
23
|
+
* One beat: ZADD the member at the server clock, write the facts, give both keys a TTL of the
|
|
24
|
+
* window and trim members older than it. KEYS: the ZSET, the replica's hash. ARGV: the replica id,
|
|
25
|
+
* the window (ms), then the hash's field / value pairs.
|
|
26
|
+
*/
|
|
27
|
+
const BEAT = `
|
|
28
|
+
local t = redis.call('TIME')
|
|
29
|
+
local now = tonumber(t[1]) * 1000 + math.floor(tonumber(t[2]) / 1000)
|
|
30
|
+
local ttl = tonumber(ARGV[2])
|
|
31
|
+
redis.call('ZADD', KEYS[1], now, ARGV[1])
|
|
32
|
+
redis.call('HSET', KEYS[2], unpack(ARGV, 3))
|
|
33
|
+
redis.call('PEXPIRE', KEYS[2], ttl)
|
|
34
|
+
redis.call('PEXPIRE', KEYS[1], ttl)
|
|
35
|
+
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', now - ttl)
|
|
36
|
+
return now
|
|
37
|
+
`;
|
|
38
|
+
/** The members newer than the window (ARGV[1], ms) by the Redis server clock. */
|
|
39
|
+
const LIVE = `
|
|
40
|
+
local t = redis.call('TIME')
|
|
41
|
+
local now = tonumber(t[1]) * 1000 + math.floor(tonumber(t[2]) / 1000)
|
|
42
|
+
return redis.call('ZRANGEBYSCORE', KEYS[1], '(' .. (now - tonumber(ARGV[1])), '+inf')
|
|
43
|
+
`;
|
|
44
|
+
const ROLES = ["api", "worker", "gateway"];
|
|
45
|
+
const count = (v) => {
|
|
46
|
+
const n = Number(v);
|
|
47
|
+
return Number.isInteger(n) && n >= 0 ? n : 0;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The replicas with a beat newer than `windowMs` (Redis server clock): one script for the members,
|
|
51
|
+
* then one pipelined HGETALL each. A member whose facts already expired is left out. Sorted by
|
|
52
|
+
* start time, then id.
|
|
53
|
+
*/
|
|
54
|
+
export async function readLiveReplicas(redis, windowMs) {
|
|
55
|
+
const ids = (await redis.eval(LIVE, 1, REPLICAS_KEY, windowMs));
|
|
56
|
+
if (ids.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
const pipe = redis.pipeline();
|
|
59
|
+
for (const id of ids)
|
|
60
|
+
pipe.hgetall(replicaKey(id));
|
|
61
|
+
const res = (await pipe.exec()) ?? [];
|
|
62
|
+
const out = [];
|
|
63
|
+
ids.forEach((id, i) => {
|
|
64
|
+
const [err, h] = res[i] ?? [null, null];
|
|
65
|
+
if (err)
|
|
66
|
+
throw err;
|
|
67
|
+
const f = h;
|
|
68
|
+
if (f === null || f.version === undefined)
|
|
69
|
+
return;
|
|
70
|
+
out.push({
|
|
71
|
+
id,
|
|
72
|
+
roles: (f.roles ?? "")
|
|
73
|
+
.split(",")
|
|
74
|
+
.filter((r) => ROLES.includes(r)),
|
|
75
|
+
version: f.version,
|
|
76
|
+
startedAt: f.startedAt ?? "",
|
|
77
|
+
httpReplicaId: f.httpReplicaId ? f.httpReplicaId : null,
|
|
78
|
+
channelsHeld: count(f.channelsHeld),
|
|
79
|
+
devicesConnected: count(f.devicesConnected),
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
return out.sort((a, b) => a.startedAt.localeCompare(b.startedAt) || a.id.localeCompare(b.id));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Replica presence (admin overview spec §5.1): every process, whatever its roles, says every
|
|
86
|
+
* {@link PRESENCE_DEFAULT_INTERVAL_MS} that it is alive, with its roles, version, start time and
|
|
87
|
+
* the connections it holds, so the admin console can tell which replicas run in which role.
|
|
88
|
+
*
|
|
89
|
+
* Multi-replica: the state lives only in Redis, with a TTL of three intervals on both keys; the
|
|
90
|
+
* score is the Redis server clock (replica clocks may drift), as in the gateway heartbeat. Each
|
|
91
|
+
* process writes only its own entry and holds nothing other processes need: no lock, no
|
|
92
|
+
* transaction, no shared job. So it is not a `@LockedInterval` (that runs once across replicas)
|
|
93
|
+
* but a `setTimeout` chain of this process (`unref`), started by `KernelLifecycle` at the end of
|
|
94
|
+
* the start (after the start gate opened) and stopped first thing in the stop, which removes the
|
|
95
|
+
* entry at once; a crashed process ages out of {@link live} within the window. A failing beat is
|
|
96
|
+
* logged at warn and never thrown.
|
|
97
|
+
*/
|
|
98
|
+
let ReplicaPresence = class ReplicaPresence {
|
|
99
|
+
redis;
|
|
100
|
+
config;
|
|
101
|
+
log;
|
|
102
|
+
/** The beat interval; the window (and the TTL) is three of them. */
|
|
103
|
+
intervalMs;
|
|
104
|
+
sources;
|
|
105
|
+
startedAt = "";
|
|
106
|
+
timer;
|
|
107
|
+
running;
|
|
108
|
+
halted = false;
|
|
109
|
+
constructor(redis, config, log, intervalMs) {
|
|
110
|
+
this.redis = redis;
|
|
111
|
+
this.config = config;
|
|
112
|
+
this.log = log;
|
|
113
|
+
this.intervalMs = intervalMs ?? PRESENCE_DEFAULT_INTERVAL_MS;
|
|
114
|
+
}
|
|
115
|
+
/** How long an entry stays live without a beat (ms). */
|
|
116
|
+
get windowMs() {
|
|
117
|
+
return this.intervalMs * 3;
|
|
118
|
+
}
|
|
119
|
+
/** Starts beating (the first beat at once). Called once, at the end of the start. */
|
|
120
|
+
start(sources) {
|
|
121
|
+
if (this.sources !== undefined || this.halted)
|
|
122
|
+
return;
|
|
123
|
+
this.sources = sources;
|
|
124
|
+
this.startedAt = new Date().toISOString();
|
|
125
|
+
this.schedule(0);
|
|
126
|
+
}
|
|
127
|
+
schedule(ms) {
|
|
128
|
+
if (this.halted)
|
|
129
|
+
return;
|
|
130
|
+
this.timer = setTimeout(() => {
|
|
131
|
+
this.running = this.beat().finally(() => {
|
|
132
|
+
this.running = undefined;
|
|
133
|
+
this.schedule(this.intervalMs);
|
|
134
|
+
});
|
|
135
|
+
}, ms);
|
|
136
|
+
this.timer.unref?.();
|
|
137
|
+
}
|
|
138
|
+
async beat() {
|
|
139
|
+
const s = this.sources;
|
|
140
|
+
if (s === undefined || this.halted)
|
|
141
|
+
return;
|
|
142
|
+
try {
|
|
143
|
+
const http = this.config.get().http.replicaId;
|
|
144
|
+
await this.redis.eval(BEAT, 2, REPLICAS_KEY, replicaKey(s.replicaId), s.replicaId, this.windowMs, "roles", this.config.get().roles.join(","), "version", KERNEL_VERSION, "startedAt", this.startedAt, "httpReplicaId", http === undefined ? "" : String(http), "channelsHeld", s.channelsHeld(), "devicesConnected", s.devicesConnected());
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
this.log.warn("replica presence beat failed", { err });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Stops beating and waits for a beat in flight, leaving the entry to age out (a crash looks like
|
|
152
|
+
* this; the tests use it as one). Idempotent.
|
|
153
|
+
*/
|
|
154
|
+
async halt() {
|
|
155
|
+
this.halted = true;
|
|
156
|
+
clearTimeout(this.timer);
|
|
157
|
+
await this.running;
|
|
158
|
+
}
|
|
159
|
+
/** The stop: {@link halt}, then removes this process's entry at once (best effort). Idempotent. */
|
|
160
|
+
async stop() {
|
|
161
|
+
await this.halt();
|
|
162
|
+
const s = this.sources;
|
|
163
|
+
if (s === undefined)
|
|
164
|
+
return;
|
|
165
|
+
this.sources = undefined;
|
|
166
|
+
try {
|
|
167
|
+
await this.redis.multi().zrem(REPLICAS_KEY, s.replicaId).del(replicaKey(s.replicaId)).exec();
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
this.log.warn("cannot remove replica presence", { err });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The live replicas (a beat within the window, by the Redis server clock), sorted by start time.
|
|
175
|
+
* Throws when Redis does not answer.
|
|
176
|
+
*/
|
|
177
|
+
live() {
|
|
178
|
+
return readLiveReplicas(this.redis, this.windowMs);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
ReplicaPresence = __decorate([
|
|
182
|
+
Injectable(),
|
|
183
|
+
__param(0, Inject(REDIS)),
|
|
184
|
+
__param(1, Inject(KernelConfigService)),
|
|
185
|
+
__param(2, Inject(KERNEL_LOGGER)),
|
|
186
|
+
__param(3, Optional()),
|
|
187
|
+
__param(3, Inject(PRESENCE_INTERVAL_MS))
|
|
188
|
+
], ReplicaPresence);
|
|
189
|
+
export { ReplicaPresence };
|
package/dist/kernel.module.js
CHANGED
|
@@ -25,6 +25,7 @@ import { databaseCheck, HEALTH_CHECKS, redisCheck } from "./infra/health/health.
|
|
|
25
25
|
import { GracefulShutdownModule } from "./infra/lifecycle/graceful-shutdown.module.js";
|
|
26
26
|
import { LockModule } from "./infra/lock/lock.module.js";
|
|
27
27
|
import { LoggingModule } from "./infra/logging/logging.module.js";
|
|
28
|
+
import { PresenceModule } from "./infra/presence/presence.module.js";
|
|
28
29
|
import { QueueModule } from "./infra/queue/queue.module.js";
|
|
29
30
|
import { RedisModule } from "./infra/redis/redis.module.js";
|
|
30
31
|
import { KernelThrottlerModule } from "./infra/throttler/throttler.module.js";
|
|
@@ -101,6 +102,8 @@ let KernelModule = KernelModule_1 = class KernelModule {
|
|
|
101
102
|
QueueModule.forRoot(),
|
|
102
103
|
KernelThrottlerModule,
|
|
103
104
|
HealthModule,
|
|
105
|
+
// every process's presence beat (admin overview spec §5.1), whatever its roles
|
|
106
|
+
PresenceModule,
|
|
104
107
|
ValidationModule,
|
|
105
108
|
ErrorFilterModule,
|
|
106
109
|
KernelAppOptionsModule.forRoot({ ...options, logger }),
|
|
@@ -21,6 +21,7 @@ import { AccountsService } from "../identity/accounts.js";
|
|
|
21
21
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
22
22
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
23
23
|
import { GracefulShutdown } from "../infra/lifecycle/graceful-shutdown.service.js";
|
|
24
|
+
import { ReplicaPresence } from "../infra/presence/replica-presence.js";
|
|
24
25
|
import type { KernelLogger } from "../log.js";
|
|
25
26
|
import { GatewayRole } from "../roles/gateway-role.js";
|
|
26
27
|
import { WorkerRole } from "../roles/worker-role.js";
|
|
@@ -83,6 +84,7 @@ export declare class KernelLifecycle implements OnApplicationBootstrap, BeforeAp
|
|
|
83
84
|
private readonly titles;
|
|
84
85
|
private readonly channelJobs;
|
|
85
86
|
private readonly channelOutbound;
|
|
87
|
+
private readonly presence;
|
|
86
88
|
private readonly worker?;
|
|
87
89
|
private readonly gateway?;
|
|
88
90
|
/** The startup config (hot fields are read through the config service). */
|
|
@@ -97,7 +99,7 @@ export declare class KernelLifecycle implements OnApplicationBootstrap, BeforeAp
|
|
|
97
99
|
private unwatchConfig;
|
|
98
100
|
/** What the first bootstrap pass did, carried into the second one. */
|
|
99
101
|
private bootstrapCarry;
|
|
100
|
-
constructor(accounts: AccountsService, configService: KernelConfigService, log: KernelLogger, options: KernelAppOptions, pool: Kysely<any>, redis: Redis, kdb: KernelDb, shutdown: GracefulShutdown, host: ExtensionHost, registry: ExtensionRegistry, installs: ExtensionInstalls, blobs: ExtensionBlobs, extensionLifecycle: ExtensionLifecycle, replicaStatus: ReplicaStatusStore, bootstrap: BootstrapService, marketplaces: MarketplacesService, pluginImport: PluginImport, bus: Pick<ExtensionContext["bus"], "publish" | "subscribe">, usageReport: UsageReporter, titles: TitleQueue, channelJobs: ChannelJobs, channelOutbound: ChannelOutbound, worker?: WorkerRole | undefined, gateway?: GatewayRole | undefined);
|
|
102
|
+
constructor(accounts: AccountsService, configService: KernelConfigService, log: KernelLogger, options: KernelAppOptions, pool: Kysely<any>, redis: Redis, kdb: KernelDb, shutdown: GracefulShutdown, host: ExtensionHost, registry: ExtensionRegistry, installs: ExtensionInstalls, blobs: ExtensionBlobs, extensionLifecycle: ExtensionLifecycle, replicaStatus: ReplicaStatusStore, bootstrap: BootstrapService, marketplaces: MarketplacesService, pluginImport: PluginImport, bus: Pick<ExtensionContext["bus"], "publish" | "subscribe">, usageReport: UsageReporter, titles: TitleQueue, channelJobs: ChannelJobs, channelOutbound: ChannelOutbound, presence: ReplicaPresence, worker?: WorkerRole | undefined, gateway?: GatewayRole | undefined);
|
|
101
103
|
/** The kernel HTTP application (set up by `buildKernelApp` before `init()`). */
|
|
102
104
|
get http(): KernelHttpApp;
|
|
103
105
|
attachHttp(http: KernelHttpApp): void;
|
|
@@ -31,6 +31,7 @@ import { waitWithin } from "../infra/close-within.js";
|
|
|
31
31
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
32
32
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
33
33
|
import { GracefulShutdown } from "../infra/lifecycle/graceful-shutdown.service.js";
|
|
34
|
+
import { ReplicaPresence } from "../infra/presence/replica-presence.js";
|
|
34
35
|
import { KERNEL_BUS, KERNEL_LOGGER, KERNEL_POOL, REDIS } from "../infra/tokens.js";
|
|
35
36
|
import { KERNEL_MIGRATIONS_DIR, KERNEL_SCHEMA } from "../kernel.js";
|
|
36
37
|
import { GatewayRole } from "../roles/gateway-role.js";
|
|
@@ -104,6 +105,7 @@ let KernelLifecycle = class KernelLifecycle {
|
|
|
104
105
|
titles;
|
|
105
106
|
channelJobs;
|
|
106
107
|
channelOutbound;
|
|
108
|
+
presence;
|
|
107
109
|
worker;
|
|
108
110
|
gateway;
|
|
109
111
|
/** The startup config (hot fields are read through the config service). */
|
|
@@ -123,6 +125,8 @@ let KernelLifecycle = class KernelLifecycle {
|
|
|
123
125
|
pool, redis, kdb, shutdown, host, registry, installs, blobs, extensionLifecycle, replicaStatus, bootstrap, marketplaces, pluginImport, bus,
|
|
124
126
|
// the producers closed on every role (their consumers are the worker role's)
|
|
125
127
|
usageReport, titles, channelJobs, channelOutbound,
|
|
128
|
+
// this process's presence beat, on every role (admin overview spec §5.1)
|
|
129
|
+
presence,
|
|
126
130
|
// The role modules' parts: a role module is imported only when `roles` names it, so each is
|
|
127
131
|
// present exactly when its role is (checked at the start)
|
|
128
132
|
worker, gateway) {
|
|
@@ -148,6 +152,7 @@ let KernelLifecycle = class KernelLifecycle {
|
|
|
148
152
|
this.titles = titles;
|
|
149
153
|
this.channelJobs = channelJobs;
|
|
150
154
|
this.channelOutbound = channelOutbound;
|
|
155
|
+
this.presence = presence;
|
|
151
156
|
this.worker = worker;
|
|
152
157
|
this.gateway = gateway;
|
|
153
158
|
this.config = configService.get();
|
|
@@ -267,6 +272,12 @@ let KernelLifecycle = class KernelLifecycle {
|
|
|
267
272
|
// 热更新:重新校验;只应用 hot 字段,其余变化 warn 并列出需重启的键;校验失败保持原配置
|
|
268
273
|
this.unwatchConfig = opts.source.watch((raw) => this.configService.apply(raw), (err) => this.log.error("config watch error", { error: err.message }));
|
|
269
274
|
}
|
|
275
|
+
// Last: the process says it is alive (in its roles) only once its start went through
|
|
276
|
+
this.presence.start({
|
|
277
|
+
replicaId: this.replicaStatus.replicaId,
|
|
278
|
+
channelsHeld: () => gateway?.channelsHeld() ?? 0,
|
|
279
|
+
devicesConnected: () => gateway?.devicesConnected() ?? 0,
|
|
280
|
+
});
|
|
270
281
|
}
|
|
271
282
|
/**
|
|
272
283
|
* Listens on `port` (0.0.0.0; every role listens: worker-only serves only `/healthz` and
|
|
@@ -379,6 +390,8 @@ let KernelLifecycle = class KernelLifecycle {
|
|
|
379
390
|
// Extension requests in progress (SSE and other long requests end on req.signal) are aborted
|
|
380
391
|
// first, otherwise they would use up the http phase
|
|
381
392
|
this.httpApp?.abortPluginRequests();
|
|
393
|
+
// Leave the replica presence first: the admin console stops counting this process at once
|
|
394
|
+
await within("presence", this.presence.stop(), 1_000);
|
|
382
395
|
// Scheduled jobs (worker role): no new tick, the runs in progress get the stop signal; awaited
|
|
383
396
|
// in the runs phase (before the extension host closes: a job may call an extension)
|
|
384
397
|
const { worker, gateway } = this;
|
|
@@ -457,9 +470,10 @@ KernelLifecycle = __decorate([
|
|
|
457
470
|
__param(19, Inject(TitleQueue)),
|
|
458
471
|
__param(20, Inject(ChannelJobs)),
|
|
459
472
|
__param(21, Inject(ChannelOutbound)),
|
|
460
|
-
__param(22,
|
|
461
|
-
__param(22, Inject(WorkerRole)),
|
|
473
|
+
__param(22, Inject(ReplicaPresence)),
|
|
462
474
|
__param(23, Optional()),
|
|
463
|
-
__param(23, Inject(
|
|
475
|
+
__param(23, Inject(WorkerRole)),
|
|
476
|
+
__param(24, Optional()),
|
|
477
|
+
__param(24, Inject(GatewayRole))
|
|
464
478
|
], KernelLifecycle);
|
|
465
479
|
export { KernelLifecycle };
|
|
@@ -120,6 +120,12 @@ export declare class LocalDeviceGateway {
|
|
|
120
120
|
authenticate(credential: string): Promise<MachineIdentity | null>;
|
|
121
121
|
/** Serves one upgraded socket of an authenticated machine until it closes. */
|
|
122
122
|
accept(socket: WebSocket, identity: MachineIdentity, credential: string): void;
|
|
123
|
+
/**
|
|
124
|
+
* How many devices this process holds the connection of (replica presence reports it): only
|
|
125
|
+
* sockets past the hello that hold their device's lease, so a socket still claiming or being
|
|
126
|
+
* replaced by a newer one does not count a device twice.
|
|
127
|
+
*/
|
|
128
|
+
connectionCount(): number;
|
|
123
129
|
/** @internal a connection that ended. */
|
|
124
130
|
forget(conn: DeviceConnection): void;
|
|
125
131
|
/** Closes every connection (1001) and hands their leases back. */
|
|
@@ -508,6 +508,18 @@ let LocalDeviceGateway = class LocalDeviceGateway {
|
|
|
508
508
|
this.connections.add(conn);
|
|
509
509
|
conn.start();
|
|
510
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* How many devices this process holds the connection of (replica presence reports it): only
|
|
513
|
+
* sockets past the hello that hold their device's lease, so a socket still claiming or being
|
|
514
|
+
* replaced by a newer one does not count a device twice.
|
|
515
|
+
*/
|
|
516
|
+
connectionCount() {
|
|
517
|
+
let n = 0;
|
|
518
|
+
for (const c of this.connections)
|
|
519
|
+
if (c.state === "holding")
|
|
520
|
+
n++;
|
|
521
|
+
return n;
|
|
522
|
+
}
|
|
511
523
|
/** @internal a connection that ended. */
|
|
512
524
|
forget(conn) {
|
|
513
525
|
this.connections.delete(conn);
|
package/dist/overrides.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { NestInterceptor } from "@nestjs/common";
|
|
2
2
|
import { APP_INTERCEPTOR } from "@nestjs/core";
|
|
3
|
+
import { ADMIN_USAGE_TIMINGS, type AdminUsageTimings } from "./admin/usage.service.js";
|
|
3
4
|
import { CHANNEL_WEBHOOK_TIMINGS, type ChannelWebhookTimings } from "./channels/webhook.js";
|
|
4
5
|
import type { EvalReplayHooks } from "./evals/run-replay.js";
|
|
5
6
|
import { EVAL_REPLAY_HOOKS } from "./evals/worker.js";
|
|
6
7
|
import { BOOTSTRAP_LOCK_TTL } from "./extensions/bootstrap.js";
|
|
7
8
|
import { FILE_PREVIEW_TIMEOUT } from "./files/previewers.js";
|
|
9
|
+
import { PRESENCE_INTERVAL_MS } from "./infra/presence/replica-presence.js";
|
|
8
10
|
import { LIST_MODELS_TIMEOUT } from "./runtime/providers.js";
|
|
9
11
|
import { RUN_CONTEXT_CLOCK } from "./runtime/run-context.service.js";
|
|
10
12
|
import { AGENT_LOOP_FACTORY, type AgentLoopFactory } from "./runtime/run-steps.js";
|
|
@@ -25,7 +27,9 @@ import { EMBED_TOKEN_CLOCK } from "./ui/embed-token.js";
|
|
|
25
27
|
* - `TOOL_PIPELINE_HOOKS`: the tool pipeline's hooks and policy decision;
|
|
26
28
|
* - `SUBAGENT_MESSAGES_HOOKS`: the sub-agent message tools' hooks (a send raced by a run's end);
|
|
27
29
|
* - `BOOTSTRAP_LOCK_TTL`, `FILE_PREVIEW_TIMEOUT`, `LIST_MODELS_TIMEOUT`, `USAGE_REPORT_RETRY`,
|
|
28
|
-
* `SANDBOX_LEASE_TIMINGS`, `CHANNEL_WEBHOOK_TIMINGS`, `SETUP_CHECK_TIMEOUT
|
|
30
|
+
* `SANDBOX_LEASE_TIMINGS`, `CHANNEL_WEBHOOK_TIMINGS`, `SETUP_CHECK_TIMEOUT`,
|
|
31
|
+
* `PRESENCE_INTERVAL_MS`: shorter timings;
|
|
32
|
+
* - `ADMIN_USAGE_TIMINGS`: the usage aggregate's statement timeout and clock;
|
|
29
33
|
* - `EMBED_TOKEN_CLOCK`: the clock embed tokens are issued and checked with;
|
|
30
34
|
* - `RUN_CONTEXT_CLOCK`: the clock run start reads the run context's date from, and the
|
|
31
35
|
* `CurrentTime` builtin the time;
|
|
@@ -44,6 +48,8 @@ interface KernelSeams {
|
|
|
44
48
|
[SANDBOX_LEASE_TIMINGS]: Partial<SandboxLeaseTimings>;
|
|
45
49
|
[CHANNEL_WEBHOOK_TIMINGS]: Partial<ChannelWebhookTimings>;
|
|
46
50
|
[SETUP_CHECK_TIMEOUT]: number;
|
|
51
|
+
[PRESENCE_INTERVAL_MS]: number;
|
|
52
|
+
[ADMIN_USAGE_TIMINGS]: Partial<AdminUsageTimings>;
|
|
47
53
|
[EMBED_TOKEN_CLOCK]: () => number;
|
|
48
54
|
[RUN_CONTEXT_CLOCK]: () => number;
|
|
49
55
|
[APP_INTERCEPTOR]: NestInterceptor;
|
package/dist/overrides.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { APP_INTERCEPTOR } from "@nestjs/core";
|
|
2
|
+
import { ADMIN_USAGE_TIMINGS } from "./admin/usage.service.js";
|
|
2
3
|
import { CHANNEL_WEBHOOK_TIMINGS } from "./channels/webhook.js";
|
|
3
4
|
import { EVAL_REPLAY_HOOKS } from "./evals/worker.js";
|
|
4
5
|
import { BOOTSTRAP_LOCK_TTL } from "./extensions/bootstrap.js";
|
|
5
6
|
import { FILE_PREVIEW_TIMEOUT } from "./files/previewers.js";
|
|
7
|
+
import { PRESENCE_INTERVAL_MS } from "./infra/presence/replica-presence.js";
|
|
6
8
|
import { LIST_MODELS_TIMEOUT } from "./runtime/providers.js";
|
|
7
9
|
import { RUN_CONTEXT_CLOCK } from "./runtime/run-context.service.js";
|
|
8
10
|
import { AGENT_LOOP_FACTORY } from "./runtime/run-steps.js";
|
|
@@ -26,6 +28,8 @@ const SEAM_TOKENS = [
|
|
|
26
28
|
SANDBOX_LEASE_TIMINGS,
|
|
27
29
|
CHANNEL_WEBHOOK_TIMINGS,
|
|
28
30
|
SETUP_CHECK_TIMEOUT,
|
|
31
|
+
PRESENCE_INTERVAL_MS,
|
|
32
|
+
ADMIN_USAGE_TIMINGS,
|
|
29
33
|
EMBED_TOKEN_CLOCK,
|
|
30
34
|
RUN_CONTEXT_CLOCK,
|
|
31
35
|
APP_INTERCEPTOR,
|
|
@@ -73,6 +73,11 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
73
73
|
category: "human-only",
|
|
74
74
|
reason: "Transfers ownership of an agent to another person",
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
route: "POST /api/v1/admin/agents/:agentId/owner",
|
|
78
|
+
category: "human-only",
|
|
79
|
+
reason: "Transfers ownership of an org's agent to another person on the platform's behalf, as the org's own transfer route",
|
|
80
|
+
},
|
|
76
81
|
// background
|
|
77
82
|
{
|
|
78
83
|
route: "GET /api/v1/orgs/:orgId/conversations/:id/background-tasks/:taskId/output",
|
|
@@ -541,6 +546,16 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
541
546
|
category: "transport",
|
|
542
547
|
reason: "Uploads a skill archive as multipart bytes",
|
|
543
548
|
},
|
|
549
|
+
{
|
|
550
|
+
route: "POST /api/v1/orgs/:orgId/skills",
|
|
551
|
+
category: "transport",
|
|
552
|
+
reason: "Creates an org skill from an uploaded archive; uploads are multipart bytes",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
route: "POST /api/v1/orgs/:orgId/agents/:agentId/skills/uploads",
|
|
556
|
+
category: "transport",
|
|
557
|
+
reason: "Uploads a skill archive as multipart bytes",
|
|
558
|
+
},
|
|
544
559
|
// approvals
|
|
545
560
|
{
|
|
546
561
|
route: "POST /api/v1/orgs/:orgId/approvals/:id/decision",
|
|
@@ -91,7 +91,7 @@ export interface PlatformOperation<I = unknown> {
|
|
|
91
91
|
mirrors: readonly string[];
|
|
92
92
|
/**
|
|
93
93
|
* Fields of the mirrored route (body, query, path params but `:orgId`) the input leaves out, e.g.
|
|
94
|
-
* `uploadId`
|
|
94
|
+
* an `uploadId` (spec §14.5 item 3; the parity test in `tools/checks` holds it).
|
|
95
95
|
*/
|
|
96
96
|
omits?: readonly string[];
|
|
97
97
|
/** Input fields the mirrored route lacks, e.g. `cursor` of a paged list. */
|
|
@@ -57,7 +57,7 @@ export const PLATFORM_DOMAINS = [
|
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
id: "admin",
|
|
60
|
-
description: "The admin console (super admin): dashboard totals and
|
|
60
|
+
description: "The admin console (super admin): dashboard totals, deployment settings, the org's members, and the agents and connectors of the org (read, delete, disable or enable; other orgs' only in the console).",
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
id: "models",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type OnModuleInit } from "@nestjs/common";
|
|
2
|
+
import { AdminAgents } from "../../agents/admin-agents.js";
|
|
3
|
+
import { AgentsService } from "../../agents/agents.service.js";
|
|
4
|
+
import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
5
|
+
import { MembersService } from "../../orgs/members.service.js";
|
|
6
|
+
import { AdminConnectors } from "../../tools/admin-connectors.js";
|
|
7
|
+
import { ConnectorsService } from "../../tools/connectors.service.js";
|
|
8
|
+
import { PlatformOperations } from "../registry.js";
|
|
9
|
+
/**
|
|
10
|
+
* Refusal of an item another org owns: the tool's results enter this org's event log, where
|
|
11
|
+
* another org's items do not belong (Platform tool spec §6.1, §14.5 item 1).
|
|
12
|
+
*/
|
|
13
|
+
export declare const OTHER_ORG_ITEM = "This belongs to another organization; the Platform tool acts only on this conversation's organization. Use the admin console.";
|
|
14
|
+
/**
|
|
15
|
+
* The super admin's agent and connector operations of the `Platform` tool (admin agents and
|
|
16
|
+
* connectors spec §4.5): the admin console's views and writes, run through the same services as
|
|
17
|
+
* the routes. Unlike the console they cover only the conversation's org: lists filter to it, and
|
|
18
|
+
* reads and writes of another org's item are refused (Platform tool spec §6.1). The owner transfer
|
|
19
|
+
* is excluded (human-only, as the org's own transfer route).
|
|
20
|
+
*/
|
|
21
|
+
export declare class AdminItemsOperations implements OnModuleInit {
|
|
22
|
+
private readonly registry;
|
|
23
|
+
private readonly adminAgents;
|
|
24
|
+
private readonly agents;
|
|
25
|
+
private readonly adminConnectors;
|
|
26
|
+
private readonly connectors;
|
|
27
|
+
private readonly members;
|
|
28
|
+
private readonly kdb;
|
|
29
|
+
constructor(registry: PlatformOperations, adminAgents: AdminAgents, agents: AgentsService, adminConnectors: AdminConnectors, connectors: ConnectorsService, members: MembersService, kdb: KernelDb);
|
|
30
|
+
/** The agent's admin detail when it is the conversation org's; else {@link OTHER_ORG_ITEM}. */
|
|
31
|
+
private agentOf;
|
|
32
|
+
/** The connector's admin detail when it is the conversation org's; else {@link OTHER_ORG_ITEM}. */
|
|
33
|
+
private connectorOf;
|
|
34
|
+
/** A precheck from a read: its `ApiError` code, or `notFound` for another org's item. */
|
|
35
|
+
private precheck;
|
|
36
|
+
onModuleInit(): void;
|
|
37
|
+
}
|