@north-light/crouter 0.3.184 → 0.3.185
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/build-root.js +10 -3
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/clients/attach/input/capabilities.js +3 -0
- package/dist/clients/attach/slash/dispatch.js +12 -0
- package/dist/clients/attach/viewer.js +191 -191
- package/dist/commands/pkg/browse/actions.d.ts +7 -0
- package/dist/commands/pkg/browse/actions.js +199 -0
- package/dist/commands/pkg/browse/catalog.d.ts +9 -0
- package/dist/commands/pkg/browse/catalog.js +466 -0
- package/dist/commands/pkg/browse/command-view.d.ts +3 -0
- package/dist/commands/pkg/browse/command-view.js +191 -0
- package/dist/commands/pkg/browse/detail-panel.d.ts +66 -0
- package/dist/commands/pkg/browse/detail-panel.js +402 -0
- package/dist/commands/pkg/browse/doc-view.d.ts +3 -0
- package/dist/commands/pkg/browse/doc-view.js +81 -0
- package/dist/commands/pkg/browse/list-panel.d.ts +28 -0
- package/dist/commands/pkg/browse/list-panel.js +168 -0
- package/dist/commands/pkg/browse/model.d.ts +230 -0
- package/dist/commands/pkg/browse/model.js +36 -0
- package/dist/commands/pkg/browse/shell.d.ts +93 -0
- package/dist/commands/pkg/browse/shell.js +640 -0
- package/dist/commands/pkg/browse.d.ts +2 -0
- package/dist/commands/pkg/browse.js +27 -0
- package/dist/commands/pkg/market-manage.js +13 -4
- package/dist/commands/pkg/plugin-manage.js +8 -3
- package/dist/commands/pkg.js +2 -1
- package/dist/commands/sys/doctor.js +6 -4
- package/dist/commands/sys/panels/broker-limits-panel.d.ts +1 -1
- package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
- package/dist/commands/sys/panels/dependencies-panel.d.ts +1 -1
- package/dist/commands/sys/panels/dependencies-panel.js +1 -1
- package/dist/commands/sys/panels/keybindings-panel.js +1 -1
- package/dist/commands/sys/panels/models-panel.d.ts +1 -1
- package/dist/commands/sys/panels/models-panel.js +1 -1
- package/dist/commands/sys/panels/plugins-panel.d.ts +1 -1
- package/dist/commands/sys/panels/plugins-panel.js +1 -1
- package/dist/commands/sys/panels/prefs-panel.d.ts +1 -1
- package/dist/commands/sys/panels/prefs-panel.js +1 -1
- package/dist/commands/sys/panels/provider-panel.d.ts +1 -1
- package/dist/commands/sys/panels/provider-panel.js +1 -1
- package/dist/commands/sys/settings-shell.js +1 -1
- package/dist/commands/sys/setup-settings-runner.d.ts +7 -18
- package/dist/commands/sys/setup-settings-runner.js +3 -52
- package/dist/commands/sys/setup-wizard.js +1 -1
- package/dist/core/__tests__/serial/command-plugins.test.js +21 -40
- package/dist/core/command-manifests/registry.d.ts +10 -3
- package/dist/core/command-manifests/registry.js +36 -22
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/discovery.js +15 -7
- package/dist/core/keybindings/catalog.d.ts +1 -1
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/resolver.d.ts +2 -2
- package/dist/core/runtime/tmux-bindings.js +2 -0
- package/dist/core/tui/page-host.d.ts +25 -0
- package/dist/core/tui/page-host.js +65 -0
- package/dist/pi-extensions/canvas-bash-valve.js +45 -1
- package/dist/types.d.ts +4 -1
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- /package/dist/{commands/sys/panels → core/tui}/panel.d.ts +0 -0
- /package/dist/{commands/sys/panels → core/tui}/panel.js +0 -0
|
@@ -3,7 +3,7 @@ import { resolve, sep } from 'node:path';
|
|
|
3
3
|
import { listInstalledPlugins, listInstalledPluginsInRoot } from '../resolver.js';
|
|
4
4
|
import { projectScopeRoots } from '../scope.js';
|
|
5
5
|
import { validateCommandManifest } from '../command-manifests/manifest.js';
|
|
6
|
-
import { resolveCommandRegistry } from '../command-manifests/registry.js';
|
|
6
|
+
import { qualifiedTopLevelCommandName, resolveCommandRegistry } from '../command-manifests/registry.js';
|
|
7
7
|
import { adaptPluginContributions } from './compose.js';
|
|
8
8
|
import { validateHttpPluginTransport } from './endpoint.js';
|
|
9
9
|
export function effectiveCommandPlugins(startDir = process.cwd(), profileId) {
|
|
@@ -139,18 +139,26 @@ export function validateEffectiveCommandPlugins(reservedNames, startDir = proces
|
|
|
139
139
|
const validations = effectiveCommandPlugins(startDir, profileId).map((plugin) => validatePluginCommands(plugin, reservedNames, coreCommandPaths));
|
|
140
140
|
const claims = new Map();
|
|
141
141
|
for (const validation of validations)
|
|
142
|
-
for (const contribution of validation.contributions)
|
|
143
|
-
|
|
142
|
+
for (const contribution of validation.contributions) {
|
|
143
|
+
const claimants = claims.get(contribution.node.name);
|
|
144
|
+
if (claimants === undefined)
|
|
145
|
+
claims.set(contribution.node.name, [contribution]);
|
|
146
|
+
else
|
|
147
|
+
claimants.push(contribution);
|
|
148
|
+
}
|
|
144
149
|
for (const [name, claimants] of claims)
|
|
145
150
|
if (claimants.length > 1)
|
|
146
|
-
for (const
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
for (const claimant of claimants) {
|
|
152
|
+
const { plugin } = claimant;
|
|
153
|
+
const qualifiedName = qualifiedTopLevelCommandName({ name: plugin.name, sourceMarketplace: plugin.sourceMarketplace }, name);
|
|
154
|
+
const validation = validations.find((candidate) => candidate.plugin.root === plugin.root);
|
|
155
|
+
validation.contributions = validation.contributions.map((contribution) => contribution === claimant ? { ...contribution, node: { ...contribution.node, name: qualifiedName } } : contribution);
|
|
156
|
+
validation.issues.push({ code: 'command_collision', plugin: plugin.name, path: name, message: `top-level command "${name}" is claimed by ${claimants.map((entry) => entry.plugin.name).join(', ')} — mounted as "${qualifiedName}"`, received: claimants.map((entry) => entry.plugin.name).join(', '), expected: 'an origin-qualified top-level command for each claimant', next: `Invoke "${qualifiedName}" to use this plugin command.` });
|
|
149
157
|
}
|
|
150
158
|
return validations;
|
|
151
159
|
}
|
|
152
160
|
function contributorIssue(plugin, issue) {
|
|
153
|
-
return { ...issue, contributor: { name: plugin.name, scope: plugin.scope } };
|
|
161
|
+
return { ...issue, contributor: { name: plugin.name, scope: plugin.scope, sourceMarketplace: plugin.sourceMarketplace } };
|
|
154
162
|
}
|
|
155
163
|
export function discoverCommandContributions(reservedNames, startDir = process.cwd(), profileId) {
|
|
156
164
|
const validations = validateEffectiveCommandPlugins(reservedNames, startDir, profileId);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BindingDefinition, SafetyRequirement } from './types.js';
|
|
2
2
|
import type { AttachBindingId, AttachMenuBindingId } from './attach-control.js';
|
|
3
|
-
type TmuxMenuAction = 'open' | 'attach' | 'show' | 'view-settings' | 'fold-tools' | 'metadata' | 'promote' | 'resume' | 'fork' | 'demote' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'recap' | 'context' | 'providers' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
|
|
3
|
+
type TmuxMenuAction = 'open' | 'attach' | 'show' | 'view-settings' | 'fold-tools' | 'metadata' | 'promote' | 'resume' | 'fork' | 'demote' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'recap' | 'context' | 'providers' | 'plugins' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
|
|
4
4
|
type TmuxMenuEntryAction = Exclude<TmuxMenuAction, 'open' | 'attach'> | `issue.${'idea' | 'bug'}`;
|
|
5
5
|
type GraphAction = 'down' | 'up' | 'first' | 'last' | 'collapse-or-manager' | 'expand-or-child' | 'focus' | 'revive' | 'focus-manager' | 'close-subtree' | 'dismiss';
|
|
6
6
|
type BrowseAction = 'quit' | 'down' | 'up' | 'first' | 'last' | 'expand-or-child' | 'collapse-or-parent' | 'toggle-fold' | 'tab-next' | 'tab-previous' | `tab.${1 | 2 | 3 | 4 | 5}` | 'close-subtree' | 'confirm-close' | 'sort' | 'scope' | 'profile' | 'residents' | 'preview' | 'search' | 'revive-now' | 'focus' | 'focus-background';
|
|
@@ -53,6 +53,7 @@ export const BINDING_CATALOG = Object.freeze([
|
|
|
53
53
|
...menuEntry('metadata', 'Show metadata', 'Show the focused node’s metadata dossier.', ['m'], ['tmux.menu.show']),
|
|
54
54
|
...menuEntry('context', 'Browse context', 'Browse the focused node context.', ['c'], ['tmux.menu.show']),
|
|
55
55
|
...menuEntry('providers', 'Open settings', 'Open the crouter settings panel.', [','], ['tmux.menu']),
|
|
56
|
+
...menuEntry('plugins', 'Browse plugins', 'Open the crouter plugin browser.', ['k'], ['tmux.menu']),
|
|
56
57
|
...menuEntry('graph', 'Canvas graph', 'Open the canvas graph.', ['g'], ['tmux.menu']),
|
|
57
58
|
...menuEntry('focus-manager', 'Go to manager', 'Focus this node’s manager.', ['h'], ['tmux.menu']),
|
|
58
59
|
...menuEntry('copy-id', 'Copy node id', 'Copy the focused node id to the system clipboard.', ['y'], ['tmux.menu']),
|
package/dist/core/resolver.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface ParsedSkillQualifier {
|
|
|
8
8
|
segments: string[];
|
|
9
9
|
}
|
|
10
10
|
export declare function parseSkillQualifier(raw: string): ParsedSkillQualifier;
|
|
11
|
-
export declare function listInstalledMarketplaces(scope: Scope): InstalledMarketplace[];
|
|
11
|
+
export declare function listInstalledMarketplaces(scope: Exclude<Scope, 'builtin'>): InstalledMarketplace[];
|
|
12
12
|
export declare function listAllMarketplaces(): InstalledMarketplace[];
|
|
13
|
-
export declare function findMarketplaceByName(name: string, scope?: Scope): InstalledMarketplace | null;
|
|
13
|
+
export declare function findMarketplaceByName(name: string, scope?: Exclude<Scope, 'builtin'>): InstalledMarketplace | null;
|
|
14
14
|
export declare function scopeRootsLabel(): string;
|
|
@@ -33,6 +33,7 @@ const LEAF_MENU_ACTIONS = {
|
|
|
33
33
|
// The settings page lays out a grouped rail beside a content panel; the
|
|
34
34
|
// default dialog-sized popup clipped both the rail and the models matrix.
|
|
35
35
|
'crtr.tmux.menu.providers': { description: 'settings', action: { kind: 'popup', run: 'sys settings', size: { w: '90%', h: '85%' } } },
|
|
36
|
+
'crtr.tmux.menu.plugins': { description: 'plugins', action: { kind: 'popup', run: 'pkg browse', size: { w: '90%', h: '85%' } } },
|
|
36
37
|
'crtr.tmux.menu.graph': { description: 'canvas graph', action: { kind: 'keys', keys: '/graph' } },
|
|
37
38
|
'crtr.tmux.menu.focus-manager': { description: 'go to manager', action: { kind: 'command', run: 'surface node focus {manager}' } },
|
|
38
39
|
'crtr.tmux.menu.copy-id': { description: 'copy node id', action: { kind: 'command', run: 'surface node id --pane {pane}' } },
|
|
@@ -301,6 +302,7 @@ function menuItems(bindings, entries) {
|
|
|
301
302
|
]));
|
|
302
303
|
addGroup('System', boundItems([
|
|
303
304
|
'crtr.tmux.menu.providers',
|
|
305
|
+
'crtr.tmux.menu.plugins',
|
|
304
306
|
'crtr.tmux.menu.issues',
|
|
305
307
|
]));
|
|
306
308
|
const prefixGestures = bindings.gestures('crtr.tmux.menu.open');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ProcessTerminal, TUI, type Component, type Focusable } from "@earendil-works/pi-tui";
|
|
2
|
+
import { readRawScopeConfig } from "../config.js";
|
|
3
|
+
import { type BindingId, type BindingResolution } from "../keybindings/index.js";
|
|
4
|
+
/** A full-screen page: a focusable TUI component that cleans up after itself. */
|
|
5
|
+
export type TuiPageSurface = Component & Focusable & {
|
|
6
|
+
dispose(): void;
|
|
7
|
+
};
|
|
8
|
+
export interface TuiPageContext {
|
|
9
|
+
tui: TUI;
|
|
10
|
+
terminal: ProcessTerminal;
|
|
11
|
+
bindings: BindingResolution<BindingId>;
|
|
12
|
+
rawUserConfig: ReturnType<typeof readRawScopeConfig>;
|
|
13
|
+
}
|
|
14
|
+
export interface TuiPageControl<Result> {
|
|
15
|
+
/** Close the page with a result. */
|
|
16
|
+
submit(result: Result): void;
|
|
17
|
+
/** Close the page with no result. */
|
|
18
|
+
cancel(): void;
|
|
19
|
+
}
|
|
20
|
+
/** Mount one page as the TUI's only child and resolve when it closes.
|
|
21
|
+
*
|
|
22
|
+
* `interceptCancel` is a process-level escape hatch for surfaces that must be
|
|
23
|
+
* abandonable while a nested component holds the keyboard; it runs before the
|
|
24
|
+
* page sees the input and closes the page when it returns true. */
|
|
25
|
+
export declare function runTuiPage<Result>(createSurface: (context: TuiPageContext, control: TuiPageControl<Result>) => TuiPageSurface, interceptCancel?: (data: string, bindings: BindingResolution<BindingId>) => boolean): Promise<Result | undefined>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// The process-level terminal host for crouter's full-screen TUI pages —
|
|
2
|
+
// `crtr sys settings`, the setup wizard, and `crtr pkg browse`.
|
|
3
|
+
//
|
|
4
|
+
// It owns exactly the concerns every page shares: theme, resolved keybindings,
|
|
5
|
+
// the raw user config those pages edit, the ProcessTerminal/TUI pair, and a
|
|
6
|
+
// restore path that survives an exception, a signal, and process exit. Anything
|
|
7
|
+
// a single page needs beyond that — pi's model runtime, a canvas client — the
|
|
8
|
+
// page's own runner builds and closes over before calling in here.
|
|
9
|
+
import { ProcessTerminal, TUI, } from "@earendil-works/pi-tui";
|
|
10
|
+
import { applyTheme } from "../../clients/attach/config.js";
|
|
11
|
+
import { readRawScopeConfig } from "../config.js";
|
|
12
|
+
import { resolveKeybindings, } from "../keybindings/index.js";
|
|
13
|
+
/** Mount one page as the TUI's only child and resolve when it closes.
|
|
14
|
+
*
|
|
15
|
+
* `interceptCancel` is a process-level escape hatch for surfaces that must be
|
|
16
|
+
* abandonable while a nested component holds the keyboard; it runs before the
|
|
17
|
+
* page sees the input and closes the page when it returns true. */
|
|
18
|
+
export async function runTuiPage(createSurface, interceptCancel) {
|
|
19
|
+
applyTheme({ cwd: process.cwd() });
|
|
20
|
+
const rawUserConfig = readRawScopeConfig("user");
|
|
21
|
+
const bindings = resolveKeybindings(rawUserConfig?.keybindings);
|
|
22
|
+
const terminal = new ProcessTerminal();
|
|
23
|
+
const tui = new TUI(terminal, true);
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let surface;
|
|
26
|
+
let restored = false;
|
|
27
|
+
const restore = () => {
|
|
28
|
+
if (restored)
|
|
29
|
+
return;
|
|
30
|
+
restored = true;
|
|
31
|
+
surface?.dispose();
|
|
32
|
+
try {
|
|
33
|
+
tui.stop();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* terminal restore */
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const finish = (result) => {
|
|
40
|
+
restore();
|
|
41
|
+
resolve(result);
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
surface = createSurface({ tui, terminal, bindings, rawUserConfig }, { submit: finish, cancel: () => finish(undefined) });
|
|
45
|
+
tui.addChild(surface);
|
|
46
|
+
tui.setFocus(surface);
|
|
47
|
+
process.once("exit", restore);
|
|
48
|
+
for (const signal of ["SIGTERM", "SIGHUP", "SIGINT"])
|
|
49
|
+
process.once(signal, () => finish(undefined));
|
|
50
|
+
if (interceptCancel !== undefined) {
|
|
51
|
+
tui.addInputListener((data) => {
|
|
52
|
+
if (!interceptCancel(data, bindings))
|
|
53
|
+
return undefined;
|
|
54
|
+
finish(undefined);
|
|
55
|
+
return { consume: true };
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
tui.start();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
restore();
|
|
62
|
+
reject(error);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -28,6 +28,7 @@ import { spawn } from 'node:child_process';
|
|
|
28
28
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
29
29
|
import { homedir } from 'node:os';
|
|
30
30
|
import { backgroundBashJob, bashJobPaths, formatBashElapsed, newBashJobId } from '../core/bash-jobs.js';
|
|
31
|
+
import { Type } from 'typebox';
|
|
31
32
|
import { createBashToolDefinition } from '@earendil-works/pi-coding-agent';
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
33
34
|
// Deadline — 5 minutes by default, overridable via CRTR_BASH_VALVE_MS
|
|
@@ -342,6 +343,49 @@ export function createValveOperations(nodeId, contextDir) {
|
|
|
342
343
|
// ---------------------------------------------------------------------------
|
|
343
344
|
// Extension
|
|
344
345
|
// ---------------------------------------------------------------------------
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// `purpose` — the one line a person reads while a command runs
|
|
348
|
+
//
|
|
349
|
+
// A deliberate deviation from taking pi's definition verbatim, and the only one
|
|
350
|
+
// here. Everywhere crtr's output reaches a non-technical audience, the host has
|
|
351
|
+
// to render SOMETHING per step, and without this field its only material is the
|
|
352
|
+
// command — which yields "Running command" for an entire turn. The model is the
|
|
353
|
+
// only party that knows why it is running this, so it says so, in one clause,
|
|
354
|
+
// before the command streams.
|
|
355
|
+
//
|
|
356
|
+
// Declared FIRST so it arrives ahead of `command` in the argument stream: a host
|
|
357
|
+
// parsing partial JSON can show the label while the command is still being
|
|
358
|
+
// written, rather than after the call is complete and about to finish.
|
|
359
|
+
//
|
|
360
|
+
// The execute path ignores it entirely — this is a labelling channel, not an
|
|
361
|
+
// input. Optional, so a model that omits it behaves exactly as before.
|
|
362
|
+
//
|
|
363
|
+
// Upstream ask: pi's own bash tool should carry this (Claude Code's bash tool
|
|
364
|
+
// has shipped an equivalent `description` field for years). Until it does, the
|
|
365
|
+
// field is re-declared here on top of pi's schema rather than forked into it.
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
const PURPOSE_DESCRIPTION = [
|
|
368
|
+
'One short clause naming what this step is FOR, in the words of the person you',
|
|
369
|
+
'are working for — "Reading the Acme thread", not "Fetching emails" and never',
|
|
370
|
+
'"Running GMAIL_FETCH_EMAILS". A person reads this while the command runs; it',
|
|
371
|
+
'is the only thing they see of this step. Under about eight words. Omit it for',
|
|
372
|
+
'steps that only touch your own scratch work.',
|
|
373
|
+
].join(' ');
|
|
374
|
+
/**
|
|
375
|
+
* Prepend an optional `purpose` to pi's bash schema, carrying pi's own property
|
|
376
|
+
* definitions through verbatim — so a future upstream change to the `command`
|
|
377
|
+
* or `timeout` wording still reaches the model, and this file owns exactly one
|
|
378
|
+
* field.
|
|
379
|
+
*/
|
|
380
|
+
function withPurpose(definition) {
|
|
381
|
+
return {
|
|
382
|
+
...definition,
|
|
383
|
+
parameters: Type.Object({
|
|
384
|
+
purpose: Type.Optional(Type.String({ description: PURPOSE_DESCRIPTION })),
|
|
385
|
+
...definition.parameters.properties,
|
|
386
|
+
}),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
345
389
|
export default function (pi) {
|
|
346
390
|
const nodeId = process.env['CRTR_NODE_ID'];
|
|
347
391
|
if (nodeId === undefined || nodeId.trim() === '')
|
|
@@ -356,6 +400,6 @@ export default function (pi) {
|
|
|
356
400
|
// every session_start is idempotent (last registration wins).
|
|
357
401
|
pi.on('session_start', (_event, ctx) => {
|
|
358
402
|
const operations = createValveOperations(nodeId, contextDir);
|
|
359
|
-
pi.registerTool(createBashToolDefinition(ctx.cwd, { operations }));
|
|
403
|
+
pi.registerTool(withPurpose(createBashToolDefinition(ctx.cwd, { operations })));
|
|
360
404
|
});
|
|
361
405
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -301,7 +301,10 @@ export interface InstalledPlugin {
|
|
|
301
301
|
}
|
|
302
302
|
export interface InstalledMarketplace {
|
|
303
303
|
name: string;
|
|
304
|
-
|
|
304
|
+
/** Never `builtin`: marketplaces are registered by the user into the user or
|
|
305
|
+
* project scope, and `listInstalledMarketplaces` has no builtin directory to
|
|
306
|
+
* read. `builtin` describes plugins that ship with crtr, not their source. */
|
|
307
|
+
scope: Exclude<Scope, 'builtin'>;
|
|
305
308
|
root: string;
|
|
306
309
|
manifest: MarketplaceManifest;
|
|
307
310
|
url: string;
|
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.185",
|
|
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.185",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
File without changes
|
|
File without changes
|