@capybara-hq/kernel 0.1.0-rc.24 → 0.1.0-rc.25
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/admin/admin.module.js +2 -0
- package/dist/admin/health.service.d.ts +2 -2
- package/dist/admin/health.service.js +6 -2
- package/dist/agents/agents.module.js +3 -0
- package/dist/agents/agents.service.d.ts +6 -1
- package/dist/agents/agents.service.js +11 -2
- package/dist/catalog/catalog-import.module.js +0 -4
- package/dist/catalog/catalog-scope.service.d.ts +63 -26
- package/dist/catalog/catalog-scope.service.js +146 -71
- package/dist/catalog/catalog.module.js +4 -3
- package/dist/catalog/catalog.service.d.ts +11 -4
- package/dist/catalog/catalog.service.js +21 -5
- package/dist/catalog/cli.d.ts +4 -3
- package/dist/catalog/cli.js +8 -13
- package/dist/catalog/job-ids.d.ts +8 -20
- package/dist/catalog/job-ids.js +11 -16
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-plugin-uploads.d.ts +11 -4
- package/dist/catalog/org-plugin-uploads.js +21 -6
- package/dist/catalog/owned-items.d.ts +6 -13
- package/dist/catalog/owned-items.js +11 -29
- package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
- package/dist/catalog/owned-plugin-cleanup.js +12 -66
- package/dist/catalog/platform-components.d.ts +56 -34
- package/dist/catalog/platform-components.js +68 -78
- package/dist/catalog/plugin-import.d.ts +28 -25
- package/dist/catalog/plugin-import.js +90 -76
- package/dist/catalog/plugin-upgrade.d.ts +4 -4
- package/dist/catalog/plugin-upgrade.js +7 -19
- package/dist/catalog/scope-jobs.d.ts +59 -0
- package/dist/catalog/scope-jobs.js +65 -0
- package/dist/catalog/scope-sync-workers.d.ts +22 -0
- package/dist/catalog/scope-sync-workers.js +58 -2
- package/dist/catalog/scope-sync.d.ts +40 -65
- package/dist/catalog/scope-sync.js +114 -122
- package/dist/channels/inbound.js +2 -2
- package/dist/channels/threads.d.ts +4 -3
- package/dist/channels/threads.js +4 -3
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +17 -5
- package/dist/conversations/conversations.service.js +17 -5
- package/dist/conversations/events.d.ts +21 -0
- package/dist/conversations/events.js +24 -1
- package/dist/conversations/source.d.ts +10 -0
- package/dist/conversations/source.js +17 -0
- package/dist/conversations/tree.d.ts +10 -0
- package/dist/conversations/tree.js +35 -0
- package/dist/db/kernel-schema.d.ts +9 -14
- package/dist/devices/devices.service.d.ts +11 -8
- package/dist/devices/devices.service.js +16 -23
- package/dist/evals/run-replay.d.ts +13 -2
- package/dist/evals/run-replay.js +28 -1
- package/dist/evals/snapshot.d.ts +7 -1
- package/dist/evals/snapshot.js +2 -1
- package/dist/extensions/hooks.d.ts +22 -3
- package/dist/extensions/hooks.js +15 -1
- package/dist/extensions/lifecycle-hooks.d.ts +99 -0
- package/dist/extensions/lifecycle-hooks.js +273 -0
- package/dist/extensions/org-config.d.ts +13 -1
- package/dist/extensions/org-config.js +19 -2
- package/dist/extensions/permission-view.d.ts +9 -5
- package/dist/extensions/permission-view.js +9 -4
- package/dist/extensions/point-defaults.d.ts +28 -1
- package/dist/extensions/point-defaults.js +44 -13
- package/dist/inbox/inbox.service.d.ts +6 -20
- package/dist/inbox/inbox.service.js +0 -3
- package/dist/inbox/org-notices.d.ts +1 -1
- package/dist/inbox/platform-inbox.service.d.ts +3 -32
- package/dist/inbox/platform-inbox.service.js +5 -54
- package/dist/infra/outbox/registry.d.ts +124 -0
- package/dist/infra/outbox/registry.js +126 -0
- package/dist/infra/queue/queue-names.d.ts +1 -1
- package/dist/infra/queue/queue-names.js +1 -0
- package/dist/orgs/directory-sync.js +1 -1
- package/dist/orgs/members.service.js +1 -1
- package/dist/orgs/offboarding.d.ts +50 -57
- package/dist/orgs/offboarding.js +117 -162
- package/dist/orgs/orgs.module.js +3 -0
- package/dist/orgs/orgs.service.d.ts +11 -5
- package/dist/orgs/orgs.service.js +15 -6
- package/dist/overrides.d.ts +3 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/ops/inbox.ops.js +4 -20
- package/dist/platform/ops/platform.ops.d.ts +1 -0
- package/dist/platform/ops/platform.ops.js +6 -3
- package/dist/roles/worker-role.d.ts +10 -5
- package/dist/roles/worker-role.js +19 -9
- package/dist/roles/worker.module.js +2 -4
- package/dist/runtime/context/build.d.ts +3 -0
- package/dist/runtime/context/build.js +15 -0
- package/dist/runtime/context/plugin-context.d.ts +20 -0
- package/dist/runtime/context/plugin-context.js +30 -0
- package/dist/runtime/langgraph-loop.js +4 -0
- package/dist/runtime/loop.d.ts +8 -0
- package/dist/runtime/maintenance.d.ts +5 -3
- package/dist/runtime/maintenance.js +36 -11
- package/dist/runtime/model-call.d.ts +6 -1
- package/dist/runtime/model-call.js +1 -0
- package/dist/runtime/plugin-context.service.d.ts +69 -0
- package/dist/runtime/plugin-context.service.js +208 -0
- package/dist/runtime/run-control.d.ts +6 -1
- package/dist/runtime/run-control.js +13 -0
- package/dist/runtime/run-queue.d.ts +13 -0
- package/dist/runtime/run-queue.js +63 -0
- package/dist/runtime/run-worker.d.ts +22 -1
- package/dist/runtime/run-worker.js +107 -10
- package/dist/runtime/runtime.module.js +2 -0
- package/dist/runtime/subagents/dispatch.js +1 -0
- package/dist/runtime/subagents/skill-fork.js +3 -0
- package/dist/sandbox/jobs.d.ts +20 -10
- package/dist/sandbox/jobs.js +23 -17
- package/dist/schedules/fire.d.ts +25 -4
- package/dist/schedules/fire.js +87 -13
- package/dist/schedules/job-ids.d.ts +66 -17
- package/dist/schedules/job-ids.js +81 -13
- package/dist/schedules/run-tracker.js +2 -1
- package/dist/schedules/scan.job.d.ts +13 -32
- package/dist/schedules/scan.job.js +35 -144
- package/dist/schedules/scheduled-tasks.service.d.ts +6 -6
- package/dist/schedules/scheduled-tasks.service.js +17 -14
- package/dist/schedules/schedules.module.js +0 -4
- package/dist/skills/changes.controller.d.ts +1 -1
- package/dist/skills/changes.controller.js +1 -1
- package/dist/tools/approvals.service.d.ts +15 -4
- package/dist/tools/approvals.service.js +26 -10
- package/dist/tools/pipeline.d.ts +11 -2
- package/dist/tools/pipeline.js +5 -0
- package/dist/tools/questions.service.d.ts +15 -4
- package/dist/tools/questions.service.js +21 -19
- package/dist/tools/registry.d.ts +8 -1
- package/dist/tools/registry.js +9 -0
- package/package.json +3 -3
- package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
- package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
- package/src/migrations/0123_pgvector.sql +27 -0
- package/src/migrations/0124_conversation_source.sql +36 -0
- package/dist/catalog/platform-migration.d.ts +0 -115
- package/dist/catalog/platform-migration.js +0 -583
- package/dist/infra/queue/add-job-once.d.ts +0 -14
- package/dist/infra/queue/add-job-once.js +0 -16
|
@@ -17,7 +17,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
17
17
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
18
18
|
import { EffectiveConfigs } from "./effective-config.js";
|
|
19
19
|
import { enabledExtensionsQuery, isExtensionEnabled } from "./enabled.js";
|
|
20
|
-
import { OrgExtensionConfigs } from "./org-config.js";
|
|
20
|
+
import { OrgConfigInvalidError, OrgExtensionConfigs } from "./org-config.js";
|
|
21
21
|
import { ExtensionRegistry } from "./registry.js";
|
|
22
22
|
/** The extension points whose provider each org chooses (`selection: "org-default"`). */
|
|
23
23
|
function selectionPoints() {
|
|
@@ -58,7 +58,7 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
58
58
|
if (!hasSelection(point)) {
|
|
59
59
|
throw new Error(`extension point ${point} has no org-default selection`);
|
|
60
60
|
}
|
|
61
|
-
const candidates = await this.candidates(orgId, point);
|
|
61
|
+
const { candidates } = await this.candidates(orgId, point);
|
|
62
62
|
const chosen = await this.choose(orgId, point, candidates);
|
|
63
63
|
if (!chosen.ok)
|
|
64
64
|
return chosen;
|
|
@@ -71,12 +71,33 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
71
71
|
config: c.config,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Every provider of `point` (the name without the major, e.g. `context`) that could serve
|
|
76
|
+
* `orgId` now, in extension-name order: the same candidates {@link resolve} chooses from (loaded
|
|
77
|
+
* here, enabled in the org, valid org and effective config, `configured()` not false), for
|
|
78
|
+
* points whose every enabled provider is used (no `selection`). `failed`: the providers enabled
|
|
79
|
+
* in the org whose status could not be read (reading a config or `configured()` threw, e.g. a
|
|
80
|
+
* transient database error); they are neither serving nor known to be off. An invalid stored
|
|
81
|
+
* org config ({@link OrgConfigInvalidError}) is not a read error: that provider is off.
|
|
82
|
+
*/
|
|
83
|
+
async providersFor(orgId, point) {
|
|
84
|
+
const { candidates, failed } = await this.candidates(orgId, point);
|
|
85
|
+
return {
|
|
86
|
+
providers: candidates.map((c) => ({
|
|
87
|
+
extension: c.extension,
|
|
88
|
+
impl: c.impl,
|
|
89
|
+
orgConfig: c.orgConfig,
|
|
90
|
+
config: c.config,
|
|
91
|
+
})),
|
|
92
|
+
failed,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
74
95
|
/** Every `org-default` point with its candidates, stored default and effective provider. */
|
|
75
96
|
async list(orgId) {
|
|
76
97
|
const stored = await this.storedDefaults(orgId);
|
|
77
98
|
const out = [];
|
|
78
99
|
for (const point of selectionPoints()) {
|
|
79
|
-
const candidates = await this.candidates(orgId, point);
|
|
100
|
+
const { candidates } = await this.candidates(orgId, point);
|
|
80
101
|
const chosen = pick(candidates, stored.get(point));
|
|
81
102
|
out.push({
|
|
82
103
|
point,
|
|
@@ -128,7 +149,11 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
128
149
|
detail: { point, extension, ...auditDetail },
|
|
129
150
|
});
|
|
130
151
|
}
|
|
131
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* The candidates of `point` in `orgId`, by extension name, and the enabled providers whose status
|
|
154
|
+
* could not be read (a read or `configured()` threw: excluded, logged). A provider whose stored
|
|
155
|
+
* org config or effective config is invalid is excluded but not `failed`: it is known to be off.
|
|
156
|
+
*/
|
|
132
157
|
async candidates(orgId, point) {
|
|
133
158
|
const db = this.kdb.db;
|
|
134
159
|
const enabled = new Set((await db
|
|
@@ -141,18 +166,21 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
141
166
|
providers.set(e.plugin, e.impl);
|
|
142
167
|
}
|
|
143
168
|
const out = [];
|
|
169
|
+
const failed = new Set();
|
|
144
170
|
for (const [extension, impl] of providers) {
|
|
145
171
|
let cfg;
|
|
146
172
|
try {
|
|
147
173
|
cfg = await this.orgConfigs.get(orgId, extension);
|
|
148
174
|
}
|
|
149
175
|
catch (err) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
// a stored org config that no longer validates is a known state: the provider is off
|
|
177
|
+
// (as for an invalid effective config below); only a read that failed is `failed`
|
|
178
|
+
const invalid = err instanceof OrgConfigInvalidError;
|
|
179
|
+
this.log.warn(invalid
|
|
180
|
+
? "extension point provider excluded, its stored org config is invalid"
|
|
181
|
+
: "extension point provider excluded, cannot read its org config", { orgId, point, extension, err });
|
|
182
|
+
if (!invalid)
|
|
183
|
+
failed.add(extension);
|
|
156
184
|
continue;
|
|
157
185
|
}
|
|
158
186
|
if (!cfg?.enabled)
|
|
@@ -163,12 +191,13 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
163
191
|
effective = await this.effective.get(orgId, extension);
|
|
164
192
|
}
|
|
165
193
|
catch (err) {
|
|
166
|
-
this.log.warn("
|
|
194
|
+
this.log.warn("extension point provider excluded, cannot read its effective config", {
|
|
167
195
|
orgId,
|
|
168
196
|
point,
|
|
169
197
|
extension,
|
|
170
198
|
err,
|
|
171
199
|
});
|
|
200
|
+
failed.add(extension);
|
|
172
201
|
continue;
|
|
173
202
|
}
|
|
174
203
|
if (!effective.ok)
|
|
@@ -181,12 +210,13 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
181
210
|
ok = configured.call(impl, cfg.config, effective.config) !== false;
|
|
182
211
|
}
|
|
183
212
|
catch (err) {
|
|
184
|
-
this.log.warn("
|
|
213
|
+
this.log.warn("extension point provider excluded, configured() threw", {
|
|
185
214
|
orgId,
|
|
186
215
|
point,
|
|
187
216
|
extension,
|
|
188
217
|
err,
|
|
189
218
|
});
|
|
219
|
+
failed.add(extension);
|
|
190
220
|
continue;
|
|
191
221
|
}
|
|
192
222
|
if (!ok)
|
|
@@ -194,7 +224,8 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
194
224
|
}
|
|
195
225
|
out.push({ extension, impl, orgConfig: cfg.config, config: effective.config });
|
|
196
226
|
}
|
|
197
|
-
|
|
227
|
+
out.sort((a, b) => (a.extension < b.extension ? -1 : a.extension > b.extension ? 1 : 0));
|
|
228
|
+
return { candidates: out, failed };
|
|
198
229
|
}
|
|
199
230
|
async choose(orgId, point, candidates) {
|
|
200
231
|
if (candidates.length < 2)
|
|
@@ -6,34 +6,20 @@ import { ApprovalsService, type ApprovalView } from "../tools/approvals.service.
|
|
|
6
6
|
import type { OrgNoticeDetails } from "./org-notices.js";
|
|
7
7
|
/** The most open org notices one inbox read returns. */
|
|
8
8
|
export declare const ORG_NOTICES_LIST_MAX = 100;
|
|
9
|
-
/** The skill the platform components migration renamed (`skill_renamed`). */
|
|
10
|
-
export interface SkillRenamedDetail {
|
|
11
|
-
skillId: string;
|
|
12
|
-
from: string;
|
|
13
|
-
to: string;
|
|
14
|
-
platformSkillId: string;
|
|
15
|
-
plugin: string | null;
|
|
16
|
-
}
|
|
17
9
|
/**
|
|
18
|
-
* A notice for the org's admins (`org_notices`), shown until one of them dismisses it
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* name a platform skill on in the org has; agents keep it, by id;
|
|
22
|
-
* - `item_shared`, `shared_item_updated`, `upload_confirmed`, `upload_rejected` (`detail`): the
|
|
23
|
-
* shapes of {@link OrgNoticeDetails} (plugin scope phase 3, P3-22).
|
|
10
|
+
* A notice for the org's admins (`org_notices`), shown until one of them dismisses it:
|
|
11
|
+
* `item_shared`, `shared_item_updated`, `upload_confirmed`, `upload_rejected`, `detail` in the
|
|
12
|
+
* shape of {@link OrgNoticeDetails} (plugin scope phase 3, P3-22).
|
|
24
13
|
*/
|
|
25
14
|
export type OrgNoticeView = {
|
|
26
15
|
id: string;
|
|
27
16
|
createdAt: string;
|
|
28
|
-
} &
|
|
29
|
-
kind: "skill_renamed";
|
|
30
|
-
skillRenamed: SkillRenamedDetail;
|
|
31
|
-
} | {
|
|
17
|
+
} & {
|
|
32
18
|
[K in keyof OrgNoticeDetails]: {
|
|
33
19
|
kind: K;
|
|
34
20
|
detail: OrgNoticeDetails[K];
|
|
35
21
|
};
|
|
36
|
-
}[keyof OrgNoticeDetails]
|
|
22
|
+
}[keyof OrgNoticeDetails];
|
|
37
23
|
/** 收件箱一次最多返回的事项数。 */
|
|
38
24
|
export declare const INBOX_MAX_ITEMS = 100;
|
|
39
25
|
/**
|
|
@@ -51,7 +37,7 @@ export declare const INBOX_MAX_ITEMS = 100;
|
|
|
51
37
|
* - `schedule_attention`: the current user's own scheduled task that needs them (spec §8): paused
|
|
52
38
|
* for any reason but their own pause, its latest run failed, or its latest run was refused tools
|
|
53
39
|
* or connections; derived live, gone once the task is resumed or runs clean;
|
|
54
|
-
* - the org notices (`
|
|
40
|
+
* - the org notices (`item_shared`, `shared_item_updated`, `upload_confirmed`,
|
|
55
41
|
* `upload_rejected`; `type` = the notice's kind): for org admins, until one dismisses it
|
|
56
42
|
* (`POST inbox/notices/:id/dismiss`).
|
|
57
43
|
*/
|
|
@@ -112,9 +112,6 @@ let InboxService = class InboxService {
|
|
|
112
112
|
.execute();
|
|
113
113
|
return rows.map((r) => {
|
|
114
114
|
const base = { id: r.id, createdAt: r.created_at.toISOString() };
|
|
115
|
-
if (r.kind === "skill_renamed") {
|
|
116
|
-
return { ...base, kind: r.kind, skillRenamed: r.detail };
|
|
117
|
-
}
|
|
118
115
|
return { ...base, kind: r.kind, detail: r.detail };
|
|
119
116
|
});
|
|
120
117
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type OwnedRef, type Owner } from "../catalog/ownership.js";
|
|
2
2
|
import type { KernelSchemaDb, OrgNoticeKind } from "../db/kernel-schema.js";
|
|
3
3
|
export type { OrgNoticeKind };
|
|
4
|
-
/** The `detail` of each notice kind
|
|
4
|
+
/** The `detail` of each notice kind. */
|
|
5
5
|
export interface OrgNoticeDetails {
|
|
6
6
|
/** An org was added to the item's list, or its scope widened (`all_orgs`). */
|
|
7
7
|
/** `displayName`: a plugin's display name, which the inbox names it by (null otherwise). */
|
|
@@ -8,11 +8,6 @@ import type { PlatformNoticeDetails } from "./platform-notices.js";
|
|
|
8
8
|
export declare const PLATFORM_INBOX_MAX_ITEMS = 100;
|
|
9
9
|
/** The most open scope sync failures one read takes. */
|
|
10
10
|
export declare const SCOPE_SYNC_FAILURES_LIST_MAX = 200;
|
|
11
|
-
/**
|
|
12
|
-
* A platform components migration run unfinished this long after its start is stuck (its worker
|
|
13
|
-
* stopped mid-run; a run takes seconds, and the next one starts at the next worker start or day).
|
|
14
|
-
*/
|
|
15
|
-
export declare const MIGRATION_RUN_STUCK_MS: number;
|
|
16
11
|
/**
|
|
17
12
|
* An open failure of the plugin scope sync: its last attempt to install the plugin in the org, to
|
|
18
13
|
* uninstall it, or to install the dependencies a platform update added (`dependencies`) failed,
|
|
@@ -34,18 +29,6 @@ export interface ScopeSyncFailureView {
|
|
|
34
29
|
message: string;
|
|
35
30
|
failedAt: string;
|
|
36
31
|
}
|
|
37
|
-
/**
|
|
38
|
-
* The latest platform components migration run (`platform_migration_runs`) when it needs a look:
|
|
39
|
-
* finished with catalog items whose components could not be written (`itemsFailed`,
|
|
40
|
-
* `<name>@<version>`), or `stuck` (unfinished {@link MIGRATION_RUN_STUCK_MS} after its start).
|
|
41
|
-
*/
|
|
42
|
-
export interface MigrationRunView {
|
|
43
|
-
id: string;
|
|
44
|
-
startedAt: string;
|
|
45
|
-
finishedAt: string | null;
|
|
46
|
-
stuck: boolean;
|
|
47
|
-
itemsFailed: string[];
|
|
48
|
-
}
|
|
49
32
|
/**
|
|
50
33
|
* One item of the super admin's inbox (by `type`). Nothing is decided in the inbox: each item is
|
|
51
34
|
* acted on where it lives (the web links to it).
|
|
@@ -56,7 +39,6 @@ export interface MigrationRunView {
|
|
|
56
39
|
* - `org_upload_pending`: an org-owned plugin whose uploaded version (with an extension) waits for
|
|
57
40
|
* the super admin in 待确认 (ruling P3-22; confirmed or rejected there), `id` = the item's;
|
|
58
41
|
* - `scope_sync_failed`: an open scope sync failure, `id` = `<itemId>/<orgId>`;
|
|
59
|
-
* - `platform_migration`: the latest migration run, failed items or stuck, `id` = the run's;
|
|
60
42
|
* - `ownership_transferred`: a deleted org's shared item now belongs to the platform, until a
|
|
61
43
|
* super admin dismisses it (`POST admin/inbox/notices/:id/dismiss`).
|
|
62
44
|
*/
|
|
@@ -80,11 +62,6 @@ export type PlatformInboxItem = {
|
|
|
80
62
|
id: string;
|
|
81
63
|
createdAt: string;
|
|
82
64
|
failure: ScopeSyncFailureView;
|
|
83
|
-
} | {
|
|
84
|
-
type: "platform_migration";
|
|
85
|
-
id: string;
|
|
86
|
-
createdAt: string;
|
|
87
|
-
run: MigrationRunView;
|
|
88
65
|
} | {
|
|
89
66
|
type: "ownership_transferred";
|
|
90
67
|
id: string;
|
|
@@ -92,7 +69,7 @@ export type PlatformInboxItem = {
|
|
|
92
69
|
notice: PlatformNoticeView;
|
|
93
70
|
};
|
|
94
71
|
/** Every {@link PlatformInboxItem} type, in the order the admin overview lists them. */
|
|
95
|
-
export declare const PLATFORM_INBOX_TYPES: readonly ["catalog_request", "permission_confirmation", "org_upload_pending", "scope_sync_failed", "
|
|
72
|
+
export declare const PLATFORM_INBOX_TYPES: readonly ["catalog_request", "permission_confirmation", "org_upload_pending", "scope_sync_failed", "ownership_transferred"];
|
|
96
73
|
/** How many items of each type wait: every type, zeros included. */
|
|
97
74
|
export type PlatformInboxCounts = Record<PlatformInboxItem["type"], number>;
|
|
98
75
|
/** An open platform notice (`platform_notices`, kind `ownership_transferred`). */
|
|
@@ -116,8 +93,8 @@ export interface PlatformInboxView {
|
|
|
116
93
|
/**
|
|
117
94
|
* The super admin's inbox (plugin scope spec §5.1 rule 5, §13.5): what waits on the platform,
|
|
118
95
|
* gathered from the sources the admin pages read (the 待确认 tab's requests and permission
|
|
119
|
-
* confirmations), plus the scope sync's open failures and the platform
|
|
120
|
-
*
|
|
96
|
+
* confirmations), plus the scope sync's open failures and the platform notices. Stateless, read
|
|
97
|
+
* live from PG.
|
|
121
98
|
*/
|
|
122
99
|
export declare class PlatformInboxService {
|
|
123
100
|
private readonly requests;
|
|
@@ -148,10 +125,4 @@ export declare class PlatformInboxService {
|
|
|
148
125
|
private countScopeSyncFailures;
|
|
149
126
|
/** The open scope sync failures ({@link failureRows}), newest first. */
|
|
150
127
|
private scopeSyncFailures;
|
|
151
|
-
/**
|
|
152
|
-
* The latest migration run when it needs a look (see {@link MigrationRunView}); null when it
|
|
153
|
-
* finished without failed items, is still running, or there was none. An earlier run's failure
|
|
154
|
-
* is not shown: each run tries every unwritten item again.
|
|
155
|
-
*/
|
|
156
|
-
private migrationRun;
|
|
157
128
|
}
|
|
@@ -17,11 +17,6 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
17
17
|
export const PLATFORM_INBOX_MAX_ITEMS = 100;
|
|
18
18
|
/** The most open scope sync failures one read takes. */
|
|
19
19
|
export const SCOPE_SYNC_FAILURES_LIST_MAX = 200;
|
|
20
|
-
/**
|
|
21
|
-
* A platform components migration run unfinished this long after its start is stuck (its worker
|
|
22
|
-
* stopped mid-run; a run takes seconds, and the next one starts at the next worker start or day).
|
|
23
|
-
*/
|
|
24
|
-
export const MIGRATION_RUN_STUCK_MS = 60 * 60_000;
|
|
25
20
|
/** Plugin install statuses of a plugin that is on in the org. */
|
|
26
21
|
const LIVE_INSTALL = ["installing", "installed", "upgrading"];
|
|
27
22
|
/** Every {@link PlatformInboxItem} type, in the order the admin overview lists them. */
|
|
@@ -30,14 +25,13 @@ export const PLATFORM_INBOX_TYPES = [
|
|
|
30
25
|
"permission_confirmation",
|
|
31
26
|
"org_upload_pending",
|
|
32
27
|
"scope_sync_failed",
|
|
33
|
-
"platform_migration",
|
|
34
28
|
"ownership_transferred",
|
|
35
29
|
];
|
|
36
30
|
/**
|
|
37
31
|
* The super admin's inbox (plugin scope spec §5.1 rule 5, §13.5): what waits on the platform,
|
|
38
32
|
* gathered from the sources the admin pages read (the 待确认 tab's requests and permission
|
|
39
|
-
* confirmations), plus the scope sync's open failures and the platform
|
|
40
|
-
*
|
|
33
|
+
* confirmations), plus the scope sync's open failures and the platform notices. Stateless, read
|
|
34
|
+
* live from PG.
|
|
41
35
|
*/
|
|
42
36
|
let PlatformInboxService = class PlatformInboxService {
|
|
43
37
|
requests;
|
|
@@ -49,13 +43,12 @@ let PlatformInboxService = class PlatformInboxService {
|
|
|
49
43
|
this.kdb = kdb;
|
|
50
44
|
}
|
|
51
45
|
async list() {
|
|
52
|
-
const [requests, requestCount, pending, failures, failureCount,
|
|
46
|
+
const [requests, requestCount, pending, failures, failureCount, notices, noticeCount] = await Promise.all([
|
|
53
47
|
this.requests.listDeployment(),
|
|
54
48
|
this.requests.countPendingDeployment(),
|
|
55
49
|
this.catalog.list({ pending: true }),
|
|
56
50
|
this.scopeSyncFailures(),
|
|
57
51
|
this.countScopeSyncFailures(),
|
|
58
|
-
this.migrationRun(),
|
|
59
52
|
this.notices(),
|
|
60
53
|
this.countNotices(),
|
|
61
54
|
]);
|
|
@@ -84,16 +77,6 @@ let PlatformInboxService = class PlatformInboxService {
|
|
|
84
77
|
createdAt: n.createdAt,
|
|
85
78
|
notice: n,
|
|
86
79
|
})),
|
|
87
|
-
...(run === null
|
|
88
|
-
? []
|
|
89
|
-
: [
|
|
90
|
-
{
|
|
91
|
-
type: "platform_migration",
|
|
92
|
-
id: run.id,
|
|
93
|
-
createdAt: run.finishedAt ?? run.startedAt,
|
|
94
|
-
run,
|
|
95
|
-
},
|
|
96
|
-
]),
|
|
97
80
|
];
|
|
98
81
|
// newest first; equal times by id (a stable order)
|
|
99
82
|
all.sort((a, b) => a.createdAt !== b.createdAt
|
|
@@ -111,7 +94,6 @@ let PlatformInboxService = class PlatformInboxService {
|
|
|
111
94
|
permission_confirmation: pending.length - orgUploads,
|
|
112
95
|
org_upload_pending: orgUploads,
|
|
113
96
|
scope_sync_failed: failureCount,
|
|
114
|
-
platform_migration: run === null ? 0 : 1,
|
|
115
97
|
ownership_transferred: noticeCount,
|
|
116
98
|
};
|
|
117
99
|
const total = Object.values(byType).reduce((a, b) => a + b, 0);
|
|
@@ -123,14 +105,13 @@ let PlatformInboxService = class PlatformInboxService {
|
|
|
123
105
|
* requests and the failures; the permission confirmations are the catalog's pending list).
|
|
124
106
|
*/
|
|
125
107
|
async count() {
|
|
126
|
-
const [requests, pending, failures,
|
|
108
|
+
const [requests, pending, failures, notices] = await Promise.all([
|
|
127
109
|
this.requests.countPendingDeployment(),
|
|
128
110
|
this.catalog.list({ pending: true }),
|
|
129
111
|
this.countScopeSyncFailures(),
|
|
130
|
-
this.migrationRun(),
|
|
131
112
|
this.countNotices(),
|
|
132
113
|
]);
|
|
133
|
-
return requests + pending.length + failures +
|
|
114
|
+
return requests + pending.length + failures + notices;
|
|
134
115
|
}
|
|
135
116
|
async countNotices() {
|
|
136
117
|
const r = await this.kdb.db
|
|
@@ -240,36 +221,6 @@ let PlatformInboxService = class PlatformInboxService {
|
|
|
240
221
|
failedAt: r.failed_at.toISOString(),
|
|
241
222
|
}));
|
|
242
223
|
}
|
|
243
|
-
/**
|
|
244
|
-
* The latest migration run when it needs a look (see {@link MigrationRunView}); null when it
|
|
245
|
-
* finished without failed items, is still running, or there was none. An earlier run's failure
|
|
246
|
-
* is not shown: each run tries every unwritten item again.
|
|
247
|
-
*/
|
|
248
|
-
async migrationRun() {
|
|
249
|
-
const run = await this.kdb.db
|
|
250
|
-
.selectFrom("platform_migration_runs")
|
|
251
|
-
.select(["id", "started_at", "finished_at", "stats"])
|
|
252
|
-
.orderBy("started_at", "desc")
|
|
253
|
-
.orderBy("id", "desc")
|
|
254
|
-
.limit(1)
|
|
255
|
-
.executeTakeFirst();
|
|
256
|
-
if (run === undefined)
|
|
257
|
-
return null;
|
|
258
|
-
const failed = run.stats.itemsFailed;
|
|
259
|
-
const itemsFailed = Array.isArray(failed)
|
|
260
|
-
? failed.filter((x) => typeof x === "string")
|
|
261
|
-
: [];
|
|
262
|
-
const stuck = run.finished_at === null && run.started_at.getTime() < Date.now() - MIGRATION_RUN_STUCK_MS;
|
|
263
|
-
if (!stuck && (run.finished_at === null || itemsFailed.length === 0))
|
|
264
|
-
return null;
|
|
265
|
-
return {
|
|
266
|
-
id: run.id,
|
|
267
|
-
startedAt: run.started_at.toISOString(),
|
|
268
|
-
finishedAt: run.finished_at?.toISOString() ?? null,
|
|
269
|
-
stuck,
|
|
270
|
-
itemsFailed,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
224
|
};
|
|
274
225
|
PlatformInboxService = __decorate([
|
|
275
226
|
Injectable(),
|
|
@@ -66,6 +66,37 @@ export declare const OUTBOX_JOBS: {
|
|
|
66
66
|
readonly removeOnFail: true;
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
|
+
readonly "run-stop": {
|
|
70
|
+
readonly queue: "cap-kernel-maintenance";
|
|
71
|
+
readonly idempotent: "stopping a finished run is a no-op";
|
|
72
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
73
|
+
readonly opts: {
|
|
74
|
+
readonly attempts: 5;
|
|
75
|
+
readonly backoff: {
|
|
76
|
+
readonly type: "exponential";
|
|
77
|
+
readonly delay: 5000;
|
|
78
|
+
};
|
|
79
|
+
readonly removeOnComplete: true;
|
|
80
|
+
readonly removeOnFail: {
|
|
81
|
+
readonly age: number;
|
|
82
|
+
readonly count: 1000;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
readonly "sandbox-destroy-for": {
|
|
87
|
+
readonly queue: "cap-kernel-maintenance";
|
|
88
|
+
readonly idempotent: "destroying a destroyed device is a no-op";
|
|
89
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
90
|
+
readonly opts: {
|
|
91
|
+
readonly attempts: 5;
|
|
92
|
+
readonly backoff: {
|
|
93
|
+
readonly type: "exponential";
|
|
94
|
+
readonly delay: 30000;
|
|
95
|
+
};
|
|
96
|
+
readonly removeOnComplete: true;
|
|
97
|
+
readonly removeOnFail: 100;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
69
100
|
readonly "deliver-to-parent": {
|
|
70
101
|
readonly queue: "cap-kernel-maintenance";
|
|
71
102
|
readonly idempotent: "the delivery's idempotency key is the child run id";
|
|
@@ -114,6 +145,99 @@ export declare const OUTBOX_JOBS: {
|
|
|
114
145
|
readonly removeOnFail: true;
|
|
115
146
|
};
|
|
116
147
|
};
|
|
148
|
+
readonly "scope-item": {
|
|
149
|
+
readonly queue: "cap-kernel-plugin-scope";
|
|
150
|
+
readonly idempotent: "the sync converges each (item, org) from the scope, switches and installs it reads under the org's lock, and the installer re-checks under the org row lock, so a second run changes nothing";
|
|
151
|
+
readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
|
|
152
|
+
readonly opts: {
|
|
153
|
+
readonly attempts: 1;
|
|
154
|
+
readonly removeOnComplete: true;
|
|
155
|
+
readonly removeOnFail: true;
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
readonly "scope-item-backstop": {
|
|
159
|
+
readonly queue: "cap-kernel-plugin-scope";
|
|
160
|
+
readonly idempotent: "the sync converges each (item, org) from what it reads, as a scope-item job does, so a run after the immediate job changes nothing";
|
|
161
|
+
readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
|
|
162
|
+
readonly opts: {
|
|
163
|
+
readonly attempts: 5;
|
|
164
|
+
readonly backoff: {
|
|
165
|
+
readonly type: "exponential";
|
|
166
|
+
readonly delay: 60000;
|
|
167
|
+
};
|
|
168
|
+
readonly removeOnComplete: true;
|
|
169
|
+
readonly removeOnFail: true;
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
readonly "scope-org": {
|
|
173
|
+
readonly queue: "cap-kernel-plugin-scope";
|
|
174
|
+
readonly idempotent: "the sync converges each (item, org) of the org from what it reads, so a second run changes nothing";
|
|
175
|
+
readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
|
|
176
|
+
readonly opts: {
|
|
177
|
+
readonly attempts: 1;
|
|
178
|
+
readonly removeOnComplete: true;
|
|
179
|
+
readonly removeOnFail: true;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
readonly "owned-plugin-cleanup": {
|
|
183
|
+
readonly queue: "cap-kernel-maintenance";
|
|
184
|
+
readonly idempotent: "the cleanup takes the extension out only while no live item uses it, and a missing install or a released claim changes nothing";
|
|
185
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
186
|
+
readonly opts: {
|
|
187
|
+
readonly attempts: 3;
|
|
188
|
+
readonly backoff: {
|
|
189
|
+
readonly type: "exponential";
|
|
190
|
+
readonly delay: 5000;
|
|
191
|
+
};
|
|
192
|
+
readonly removeOnComplete: true;
|
|
193
|
+
readonly removeOnFail: true;
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
readonly "sched-fire": {
|
|
197
|
+
readonly queue: "cap-kernel-maintenance";
|
|
198
|
+
readonly idempotent: "a fire acts only on a pending task run, under its row lock; a retried start reuses what the first one made";
|
|
199
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
200
|
+
readonly opts: {
|
|
201
|
+
readonly attempts: 3;
|
|
202
|
+
readonly backoff: {
|
|
203
|
+
readonly type: "exponential";
|
|
204
|
+
readonly delay: 2000;
|
|
205
|
+
};
|
|
206
|
+
readonly removeOnComplete: true;
|
|
207
|
+
readonly removeOnFail: true;
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
readonly "sched-settle": {
|
|
211
|
+
readonly queue: "cap-kernel-maintenance";
|
|
212
|
+
readonly idempotent: "a check acts only on a task run still pending (claimed for the claimed check, unclaimed for the unfired one), under the task and run row locks, so a run claimed, started, ended or skipped since is left as it is";
|
|
213
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
214
|
+
readonly opts: {
|
|
215
|
+
readonly attempts: 5;
|
|
216
|
+
readonly backoff: {
|
|
217
|
+
readonly type: "exponential";
|
|
218
|
+
readonly delay: 5000;
|
|
219
|
+
};
|
|
220
|
+
readonly removeOnComplete: true;
|
|
221
|
+
readonly removeOnFail: true;
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
readonly lifecycle: {
|
|
225
|
+
readonly queue: "cap-kernel-lifecycle";
|
|
226
|
+
readonly idempotent: "lifecycle handlers must be idempotent (the SDK contract): they delete what the plugin holds for the removed member, agent or org, and deleting it again is a no-op";
|
|
227
|
+
readonly processedIn: "packages/kernel/src/extensions/lifecycle-hooks.ts";
|
|
228
|
+
readonly opts: {
|
|
229
|
+
readonly attempts: 15;
|
|
230
|
+
readonly backoff: {
|
|
231
|
+
readonly type: "exponential";
|
|
232
|
+
readonly delay: 2000;
|
|
233
|
+
};
|
|
234
|
+
readonly removeOnComplete: true;
|
|
235
|
+
readonly removeOnFail: {
|
|
236
|
+
readonly age: number;
|
|
237
|
+
readonly count: 1000;
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
};
|
|
117
241
|
};
|
|
118
242
|
export type OutboxJobName = keyof typeof OUTBOX_JOBS;
|
|
119
243
|
/** The registry's BullMQ options of a job name (none for a name it does not list). */
|