@north-light/crouter 0.3.310 → 0.3.311
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/builtin-pi-packages/pi-crtr-extensions/package-lock.json +3 -3
- package/dist/clients/attach/viewer.js +483 -483
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.d.ts +1 -0
- package/dist/commands/__tests__/integration/dashboard-snapshot.test.js +212 -0
- package/dist/commands/api-client.d.ts +1 -0
- package/dist/commands/api-client.js +4 -2
- package/dist/commands/canvas-roster.js +24 -17
- package/dist/commands/dashboard.js +23 -16
- package/dist/core/__tests__/cron-timeout-descendants.test.d.ts +1 -0
- package/dist/core/__tests__/cron-timeout-descendants.test.js +102 -0
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +24 -11
- package/dist/core/canvas/remote-canvas-source.d.ts +4 -0
- package/dist/core/canvas/remote-canvas-source.js +58 -6
- package/dist/core/canvas/render-source.d.ts +4 -4
- package/dist/core/canvas/render-source.js +49 -48
- package/dist/core/canvas/source.d.ts +2 -0
- package/dist/core/canvas/source.js +4 -1
- package/dist/daemon/cron-run.js +49 -15
- package/package.json +1 -1
- package/runtime.lock.json +5 -5
|
@@ -13,8 +13,8 @@ export interface NodeTelemetry {
|
|
|
13
13
|
export declare function readNodeTelemetry(nodeId: string): NodeTelemetry;
|
|
14
14
|
/** Format a token count as `Nk` (rounded down to nearest 1 k). */
|
|
15
15
|
export declare function fmtCtx(tokensIn: number | undefined): string;
|
|
16
|
-
export declare function renderTreeFromSource(source: CanvasSource, rootId: string): Promise<string>;
|
|
17
|
-
export declare function renderForestFromSource(source: CanvasSource): Promise<string>;
|
|
16
|
+
export declare function renderTreeFromSource(source: CanvasSource, rootId: string, snapshot?: readonly NodeRow[], ticketSnapshot?: Record<string, number>, ticketUnavailable?: string): Promise<string>;
|
|
17
|
+
export declare function renderForestFromSource(source: CanvasSource, snapshot?: readonly NodeRow[], ticketSnapshot?: Record<string, number>, ticketUnavailable?: string): Promise<string>;
|
|
18
18
|
export interface DashboardRow {
|
|
19
19
|
node_id: string;
|
|
20
20
|
/** Full display label: explicit handle plus generated description. */
|
|
@@ -183,13 +183,13 @@ export declare function dashboardRowFromNodeRow(row: NodeRow, focusedNodeIds: Re
|
|
|
183
183
|
* entirely through the source — the openDb-free sibling of `dashboardRows`.
|
|
184
184
|
* Ticket counts come from ONE `source.ticketCountsForView(rootId)` call (the
|
|
185
185
|
* API/remote attention projection), never `countTickets` (openDb). */
|
|
186
|
-
export declare function dashboardRowsFromSource(source: CanvasSource, rootId: string): Promise<DashboardRow[]>;
|
|
186
|
+
export declare function dashboardRowsFromSource(source: CanvasSource, rootId: string, snapshot?: readonly NodeRow[], ticketSnapshot?: Record<string, number>): Promise<DashboardRow[]>;
|
|
187
187
|
/** CHEAP boot builder over a source — one row per node across the whole canvas,
|
|
188
188
|
* fields that need no per-node meta/session read. Local API callers may supply
|
|
189
189
|
* the node ids from one `listFocuses()` query so `viewed` remains visible without
|
|
190
190
|
* coupling the canvas source to local viewer state. Remote sources ignore that
|
|
191
191
|
* set. Local disk telemetry/fault/streaming are likewise suppressed remotely. */
|
|
192
|
-
export declare function dashboardRowsAllFromSource(source: CanvasSource, localFocusedNodeIds?: ReadonlySet<string>, liveOnly?: boolean): Promise<DashboardRow[]>;
|
|
192
|
+
export declare function dashboardRowsAllFromSource(source: CanvasSource, localFocusedNodeIds?: ReadonlySet<string>, liveOnly?: boolean, snapshot?: readonly NodeRow[]): Promise<DashboardRow[]>;
|
|
193
193
|
export declare function enrichRowsFromSource(_source: CanvasSource, rows: DashboardRow[], asks?: Record<string, number>): Promise<void>;
|
|
194
194
|
/** goal (initial-prompt.md) and session parts are local disk reads — suppressed
|
|
195
195
|
* to undefined for a remote source. */
|
|
@@ -60,9 +60,8 @@ export function fmtCtx(tokensIn) {
|
|
|
60
60
|
// LOCAL disk reads (telemetry.json / fault.json), valid for a same-host source
|
|
61
61
|
// but meaningless for a truly remote canvas, so both are suppressed when the
|
|
62
62
|
// source is a `RemoteCanvasSource`.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (node === null)
|
|
63
|
+
function nodeLineFromSource(node, nodeId, indent, connector, remote, tickets) {
|
|
64
|
+
if (node === undefined)
|
|
66
65
|
return `${indent}${connector}? <missing meta: ${nodeId}>`;
|
|
67
66
|
const hanging = remote ? null : hangingFor(node);
|
|
68
67
|
const glyph = resolveNodeVisual(node.status, { hanging, frozen: isFrozenRow(node) }).glyph;
|
|
@@ -72,12 +71,12 @@ async function nodeLineFromSource(source, nodeId, indent, connector, remote, tic
|
|
|
72
71
|
const faultSuffix = hanging !== null ? ` · ${faultSummary(hanging)}` : '';
|
|
73
72
|
return `${indent}${connector}${glyph} ${fullName(node)} [${node.kind}/${node.mode}] ctx ${ctx}${askSuffix}${faultSuffix}`;
|
|
74
73
|
}
|
|
75
|
-
async function walkTreeFromSource(source, nodeId, indent, isLast, visited, out, remote, tickets) {
|
|
74
|
+
async function walkTreeFromSource(source, nodes, nodeId, indent, isLast, visited, out, remote, tickets) {
|
|
76
75
|
if (visited.has(nodeId))
|
|
77
76
|
return;
|
|
78
77
|
visited.add(nodeId);
|
|
79
78
|
const connector = isLast ? '└─ ' : '├─ ';
|
|
80
|
-
out.push(
|
|
79
|
+
out.push(nodeLineFromSource(nodes.get(nodeId), nodeId, indent, connector, remote, tickets));
|
|
81
80
|
const childIndent = indent + (isLast ? ' ' : '│ ');
|
|
82
81
|
let children;
|
|
83
82
|
try {
|
|
@@ -95,22 +94,25 @@ async function walkTreeFromSource(source, nodeId, indent, isLast, visited, out,
|
|
|
95
94
|
out.push(`${childIndent}${cycleConnector}↺ <cycle: ${child.node_id}>`);
|
|
96
95
|
continue;
|
|
97
96
|
}
|
|
98
|
-
await walkTreeFromSource(source, child.node_id, childIndent, childIsLast, visited, out, remote, tickets);
|
|
97
|
+
await walkTreeFromSource(source, nodes, child.node_id, childIndent, childIsLast, visited, out, remote, tickets);
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
|
-
export async function renderTreeFromSource(source, rootId) {
|
|
100
|
+
export async function renderTreeFromSource(source, rootId, snapshot, ticketSnapshot, ticketUnavailable) {
|
|
102
101
|
const remote = source instanceof RemoteCanvasSource;
|
|
103
|
-
const
|
|
104
|
-
|
|
102
|
+
const nodes = new Map((snapshot ?? await source.listNodes()).map((node) => [node.node_id, node]));
|
|
103
|
+
const node = nodes.get(rootId);
|
|
104
|
+
if (node === undefined)
|
|
105
105
|
return `? <missing node: ${rootId}>`;
|
|
106
|
-
let tickets;
|
|
107
|
-
let ticketsUnavailable = null;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
let tickets = ticketSnapshot;
|
|
107
|
+
let ticketsUnavailable = ticketUnavailable ?? null;
|
|
108
|
+
if (tickets === undefined) {
|
|
109
|
+
try {
|
|
110
|
+
tickets = await source.ticketCountsForView(rootId);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
tickets = {};
|
|
114
|
+
ticketsUnavailable = err instanceof Error ? err.message : String(err);
|
|
115
|
+
}
|
|
114
116
|
}
|
|
115
117
|
const ctx = remote ? '0k' : fmtCtx(readNodeTelemetry(rootId).tokens_in);
|
|
116
118
|
const asks = tickets[rootId] ?? 0;
|
|
@@ -132,14 +134,14 @@ export async function renderTreeFromSource(source, rootId) {
|
|
|
132
134
|
for (let i = 0; i < children.length; i++) {
|
|
133
135
|
const child = children[i];
|
|
134
136
|
const isLast = i === children.length - 1;
|
|
135
|
-
await walkTreeFromSource(source, child.node_id, '', isLast, visited, out, remote, tickets);
|
|
137
|
+
await walkTreeFromSource(source, nodes, child.node_id, '', isLast, visited, out, remote, tickets);
|
|
136
138
|
}
|
|
137
139
|
if (ticketsUnavailable !== null)
|
|
138
140
|
out.push(`Tickets: unavailable (${ticketsUnavailable}).`);
|
|
139
141
|
return out.join('\n');
|
|
140
142
|
}
|
|
141
|
-
export async function renderForestFromSource(source) {
|
|
142
|
-
const all = await source.listNodes();
|
|
143
|
+
export async function renderForestFromSource(source, snapshot, ticketSnapshot, ticketUnavailable) {
|
|
144
|
+
const all = snapshot ?? await source.listNodes();
|
|
143
145
|
if (all.length === 0)
|
|
144
146
|
return '(canvas is empty)';
|
|
145
147
|
const roots = all.filter((n) => n.parent === null && (n.status === 'active' || n.status === 'idle'));
|
|
@@ -147,7 +149,7 @@ export async function renderForestFromSource(source) {
|
|
|
147
149
|
return '(no live roots)';
|
|
148
150
|
const parts = [];
|
|
149
151
|
for (const r of roots) {
|
|
150
|
-
parts.push(await renderTreeFromSource(source, r.node_id));
|
|
152
|
+
parts.push(await renderTreeFromSource(source, r.node_id, all, ticketSnapshot, ticketUnavailable));
|
|
151
153
|
}
|
|
152
154
|
return parts.join('\n\n');
|
|
153
155
|
}
|
|
@@ -563,44 +565,43 @@ export function dashboardRowFromNodeRow(row, focusedNodeIds, remote) {
|
|
|
563
565
|
* entirely through the source — the openDb-free sibling of `dashboardRows`.
|
|
564
566
|
* Ticket counts come from ONE `source.ticketCountsForView(rootId)` call (the
|
|
565
567
|
* API/remote attention projection), never `countTickets` (openDb). */
|
|
566
|
-
export async function dashboardRowsFromSource(source, rootId) {
|
|
568
|
+
export async function dashboardRowsFromSource(source, rootId, snapshot, ticketSnapshot) {
|
|
567
569
|
const remote = source instanceof RemoteCanvasSource;
|
|
568
|
-
const tickets = await source.ticketCountsForView(rootId).catch(() => ({}));
|
|
570
|
+
const tickets = ticketSnapshot ?? await source.ticketCountsForView(rootId).catch(() => ({}));
|
|
571
|
+
const nodes = new Map((snapshot ?? await source.listNodes()).map((node) => [node.node_id, node]));
|
|
569
572
|
const ids = [rootId, ...(await source.view(rootId))];
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
continue;
|
|
573
|
+
return ids.flatMap((id) => {
|
|
574
|
+
const node = nodes.get(id);
|
|
575
|
+
if (node === undefined)
|
|
576
|
+
return [];
|
|
575
577
|
const tel = remote ? {} : readNodeTelemetry(id);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
return rows;
|
|
578
|
+
return [{
|
|
579
|
+
node_id: id,
|
|
580
|
+
name: fullName(node),
|
|
581
|
+
handle: node.name,
|
|
582
|
+
description: node.description,
|
|
583
|
+
status: node.status,
|
|
584
|
+
frozen_at: node.frozen_at ?? null,
|
|
585
|
+
terminal_reason: node.terminal_reason ?? null,
|
|
586
|
+
kind: node.kind,
|
|
587
|
+
mode: node.mode,
|
|
588
|
+
ctx_tokens: tel.tokens_in ?? 0,
|
|
589
|
+
asks: tickets[id] ?? 0,
|
|
590
|
+
cwd: node.cwd,
|
|
591
|
+
profile_id: node.profile_id,
|
|
592
|
+
created: node.created,
|
|
593
|
+
}];
|
|
594
|
+
});
|
|
594
595
|
}
|
|
595
596
|
/** CHEAP boot builder over a source — one row per node across the whole canvas,
|
|
596
597
|
* fields that need no per-node meta/session read. Local API callers may supply
|
|
597
598
|
* the node ids from one `listFocuses()` query so `viewed` remains visible without
|
|
598
599
|
* coupling the canvas source to local viewer state. Remote sources ignore that
|
|
599
600
|
* set. Local disk telemetry/fault/streaming are likewise suppressed remotely. */
|
|
600
|
-
export async function dashboardRowsAllFromSource(source, localFocusedNodeIds = new Set(), liveOnly = false) {
|
|
601
|
+
export async function dashboardRowsAllFromSource(source, localFocusedNodeIds = new Set(), liveOnly = false, snapshot) {
|
|
601
602
|
const remote = source instanceof RemoteCanvasSource;
|
|
602
603
|
const focusedNodeIds = remote ? new Set() : localFocusedNodeIds;
|
|
603
|
-
const rows = await source.listNodes(liveOnly ? { status: ['active', 'idle'] } : undefined);
|
|
604
|
+
const rows = snapshot ?? await source.listNodes(liveOnly ? { status: ['active', 'idle'] } : undefined);
|
|
604
605
|
return rows.map((row) => dashboardRowFromNodeRow(row, focusedNodeIds, remote));
|
|
605
606
|
}
|
|
606
607
|
// Lazy enrichment (source-driven) — fold the per-node reads the cheap boot skips
|
|
@@ -9,6 +9,7 @@ export interface CanvasSource {
|
|
|
9
9
|
subscriptionsOf(subscriber: string): Promise<SubscriptionRef[]>;
|
|
10
10
|
subscribersOf(publisher: string): Promise<SubscriptionRef[]>;
|
|
11
11
|
view(root: string): Promise<string[]>;
|
|
12
|
+
ticketCountsForNodes(nodeIds: readonly string[]): Promise<Record<string, number>>;
|
|
12
13
|
ticketCountsForView(root: string): Promise<Record<string, number>>;
|
|
13
14
|
hasActiveLiveSubscription(nodeId: string): Promise<boolean>;
|
|
14
15
|
}
|
|
@@ -21,6 +22,7 @@ export declare class LocalCanvasSource implements CanvasSource {
|
|
|
21
22
|
subscriptionsOf(subscriber: string): Promise<SubscriptionRef[]>;
|
|
22
23
|
subscribersOf(publisher: string): Promise<SubscriptionRef[]>;
|
|
23
24
|
view(root: string): Promise<string[]>;
|
|
25
|
+
ticketCountsForNodes(nodeIds: readonly string[]): Promise<Record<string, number>>;
|
|
24
26
|
ticketCountsForView(root: string): Promise<Record<string, number>>;
|
|
25
27
|
hasActiveLiveSubscription(nodeId: string): Promise<boolean>;
|
|
26
28
|
}
|
|
@@ -22,8 +22,11 @@ export class LocalCanvasSource {
|
|
|
22
22
|
async view(root) {
|
|
23
23
|
return view(root);
|
|
24
24
|
}
|
|
25
|
+
async ticketCountsForNodes(nodeIds) {
|
|
26
|
+
return ticketCountsForNodes([...nodeIds]);
|
|
27
|
+
}
|
|
25
28
|
async ticketCountsForView(root) {
|
|
26
|
-
return ticketCountsForNodes([root, ...view(root)]);
|
|
29
|
+
return this.ticketCountsForNodes([root, ...view(root)]);
|
|
27
30
|
}
|
|
28
31
|
async hasActiveLiveSubscription(nodeId) {
|
|
29
32
|
return hasActiveLiveSubscription(nodeId);
|
package/dist/daemon/cron-run.js
CHANGED
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
// promises for graceful-teardown drain but never blocks on a slow run.
|
|
36
36
|
import { spawn } from 'node:child_process';
|
|
37
37
|
import { createHash, randomUUID } from 'node:crypto';
|
|
38
|
-
import { CRON_KILL_GRACE_MS, CRON_OUTPUT_HEAD_BYTES, EMPTY_STDOUT_SHA256, newHeadCapture, } from './cron/capture.js';
|
|
38
|
+
import { CRON_DEATH_POLL_MS, CRON_KILL_GRACE_MS, CRON_OUTPUT_HEAD_BYTES, EMPTY_STDOUT_SHA256, newHeadCapture, } from './cron/capture.js';
|
|
39
39
|
import { clearLiveRun, cronDaemonInstanceId, liveRunFor, registerLiveRun } from './cron/live-runs.js';
|
|
40
40
|
import { buildArmedRunContext } from './cron/armed-context.js';
|
|
41
41
|
import { decideHeldSettle, EXIT_HELD, HELD_DELIVERED } from './cron/held.js';
|
|
42
42
|
import { settleRun } from './cron/settle.js';
|
|
43
43
|
import { applyOverlapPolicies, expireDueCrons, } from './cron/passes.js';
|
|
44
44
|
import { dueClockCrons, acquireCronRunLease, advanceCron, setCronState, } from '../core/canvas/crons.js';
|
|
45
|
-
import {
|
|
45
|
+
import { captureTeardownSnapshot, isAnyPidAlive, killProcessTreePids } from '../core/canvas/pid.js';
|
|
46
46
|
import { emitEvent } from '../core/events/emit.js';
|
|
47
47
|
import { nextSlotAfter } from '../core/wake.js';
|
|
48
48
|
import { resolveAdmittedHostCommand } from '../core/runtime/spawn-env.js';
|
|
@@ -239,6 +239,9 @@ export function executeCron(c, opts) {
|
|
|
239
239
|
let settled = false;
|
|
240
240
|
let timer;
|
|
241
241
|
let killTimer;
|
|
242
|
+
let deathTimer;
|
|
243
|
+
let closeCode;
|
|
244
|
+
let killed = false;
|
|
242
245
|
let timedOut = false;
|
|
243
246
|
let leaseLost = false;
|
|
244
247
|
let processError = null;
|
|
@@ -259,19 +262,8 @@ export function executeCron(c, opts) {
|
|
|
259
262
|
child.stderr?.on('data', (chunk) => {
|
|
260
263
|
stderr.take(chunk);
|
|
261
264
|
});
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (killTimer === undefined) {
|
|
265
|
-
killTimer = setTimeout(() => killProcessGroup(pid, 'SIGKILL'), CRON_KILL_GRACE_MS);
|
|
266
|
-
killTimer.unref?.();
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
// A signal, not settlement: 'close' always follows 'error'.
|
|
270
|
-
child.on('error', (err) => {
|
|
271
|
-
if (processError == null)
|
|
272
|
-
processError = err;
|
|
273
|
-
});
|
|
274
|
-
child.on('close', (code) => {
|
|
265
|
+
let teardown;
|
|
266
|
+
const settleClosedChild = (code) => {
|
|
275
267
|
if (settled)
|
|
276
268
|
return;
|
|
277
269
|
settled = true;
|
|
@@ -279,6 +271,8 @@ export function executeCron(c, opts) {
|
|
|
279
271
|
clearTimeout(timer);
|
|
280
272
|
if (killTimer !== undefined)
|
|
281
273
|
clearTimeout(killTimer);
|
|
274
|
+
if (deathTimer !== undefined)
|
|
275
|
+
clearTimeout(deathTimer);
|
|
282
276
|
const stdoutHead = stdout.decode();
|
|
283
277
|
const stderrHead = stderr.decode();
|
|
284
278
|
const live = liveRunFor(c.cron_id);
|
|
@@ -306,6 +300,46 @@ export function executeCron(c, opts) {
|
|
|
306
300
|
wasReplaced,
|
|
307
301
|
wasSelfCanceled,
|
|
308
302
|
}).then(resolve, reject);
|
|
303
|
+
};
|
|
304
|
+
const awaitTeardownDeath = () => {
|
|
305
|
+
if (teardown === undefined || !isAnyPidAlive(teardown.tree)) {
|
|
306
|
+
if (closeCode !== undefined)
|
|
307
|
+
settleClosedChild(closeCode);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
deathTimer = setTimeout(awaitTeardownDeath, CRON_DEATH_POLL_MS);
|
|
311
|
+
};
|
|
312
|
+
const killWithEscalation = (pid) => {
|
|
313
|
+
teardown ??= captureTeardownSnapshot(pid, null);
|
|
314
|
+
killProcessTreePids(teardown.tree, 'SIGTERM', teardown.identities);
|
|
315
|
+
if (killTimer === undefined) {
|
|
316
|
+
killTimer = setTimeout(() => {
|
|
317
|
+
if (isAnyPidAlive(teardown.tree))
|
|
318
|
+
killProcessTreePids(teardown.tree, 'SIGKILL', teardown.identities);
|
|
319
|
+
killed = true;
|
|
320
|
+
if (closeCode !== undefined)
|
|
321
|
+
awaitTeardownDeath();
|
|
322
|
+
}, CRON_KILL_GRACE_MS);
|
|
323
|
+
killTimer.unref?.();
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
// A signal, not settlement: 'close' always follows 'error'.
|
|
327
|
+
child.on('error', (err) => {
|
|
328
|
+
if (processError == null)
|
|
329
|
+
processError = err;
|
|
330
|
+
});
|
|
331
|
+
child.on('close', (code) => {
|
|
332
|
+
if (settled)
|
|
333
|
+
return;
|
|
334
|
+
closeCode = code;
|
|
335
|
+
if (timer !== undefined)
|
|
336
|
+
clearTimeout(timer);
|
|
337
|
+
if (teardown !== undefined && isAnyPidAlive(teardown.tree)) {
|
|
338
|
+
if (killed)
|
|
339
|
+
awaitTeardownDeath();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
settleClosedChild(code);
|
|
309
343
|
});
|
|
310
344
|
if (child.pid == null ||
|
|
311
345
|
!acquireCronRunLease(c.cron_id, cronDaemonInstanceId, child.pid, startedAtIso, outOfBand)) {
|
package/package.json
CHANGED
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.311",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.311",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"workspaces": [
|
|
@@ -5206,17 +5206,17 @@
|
|
|
5206
5206
|
},
|
|
5207
5207
|
"packages/crouter-api": {
|
|
5208
5208
|
"name": "@north-light/crouter-api",
|
|
5209
|
-
"version": "0.3.
|
|
5209
|
+
"version": "0.3.311",
|
|
5210
5210
|
"license": "UNLICENSED"
|
|
5211
5211
|
},
|
|
5212
5212
|
"packages/crouter-env-docker": {
|
|
5213
5213
|
"name": "@north-light/crouter-env-docker",
|
|
5214
|
-
"version": "0.3.
|
|
5214
|
+
"version": "0.3.311",
|
|
5215
5215
|
"license": "UNLICENSED"
|
|
5216
5216
|
},
|
|
5217
5217
|
"packages/crouter-sdk": {
|
|
5218
5218
|
"name": "@north-light/crouter-sdk",
|
|
5219
|
-
"version": "0.3.
|
|
5219
|
+
"version": "0.3.311",
|
|
5220
5220
|
"license": "UNLICENSED",
|
|
5221
5221
|
"dependencies": {
|
|
5222
5222
|
"@north-light/crouter-api": "^0.3.295"
|