@dsh-workbench/adapter-supply 1.1.14-beta.2 → 1.1.15
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/bundle-manager.d.ts +11 -0
- package/dist/bundle-manager.js +181 -0
- package/dist/dsh-plugins.d.ts +5 -0
- package/dist/dsh-plugins.js +51 -0
- package/dist/installed-module.d.ts +51 -0
- package/dist/installed-module.js +265 -0
- package/dist/package-content.d.ts +6 -0
- package/dist/package-content.js +64 -0
- package/dist/package-kind.d.ts +21 -0
- package/dist/package-kind.js +73 -0
- package/dist/package-route.d.ts +3 -3
- package/dist/package-route.js +1 -2
- package/dist/plugin-catalog.d.ts +2 -2
- package/dist/plugin-catalog.js +8 -8
- package/dist/plugin-units.js +4 -3
- package/dist/registry-declaration.d.ts +3 -3
- package/dist/registry-declaration.js +2 -3
- package/package.json +3 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
+
import type { BundleCancellation, BundleChange, BundleInspection, BundleInstallRequest, BundleRegistries, BundleRegistry } from '@dsh-workbench/shared/contract/protocol/bundle-install';
|
|
3
|
+
export interface BundleManager {
|
|
4
|
+
registries(): Promise<BundleRegistries | null>;
|
|
5
|
+
inspect(spec: string, registry?: BundleRegistry): Promise<BundleInspection | null>;
|
|
6
|
+
install(request: BundleInstallRequest): Promise<BundleChange | null>;
|
|
7
|
+
remove(name: string): Promise<BundleChange | null>;
|
|
8
|
+
setEnabled?(name: string, enabled: boolean): Promise<BundleChange | null>;
|
|
9
|
+
cancel(requestId: string): Promise<BundleCancellation | null>;
|
|
10
|
+
}
|
|
11
|
+
export declare function readBundleManager(ctx: DshHostContext): BundleManager | undefined;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { RESTART_REASONS } from '@dsh-workbench/shared/contract/protocol/bundle-install';
|
|
2
|
+
const MANAGER_SERVICE = 'pluginManager';
|
|
3
|
+
const APPLICATIONS = ['applied', 'restart-required', 'overridden', 'failed', 'cancelled'];
|
|
4
|
+
const STAGES = ['install', 'enable', 'remove'];
|
|
5
|
+
const PROBLEMS = [
|
|
6
|
+
'invalid-spec',
|
|
7
|
+
'already-installed',
|
|
8
|
+
'not-found',
|
|
9
|
+
'not-a-package',
|
|
10
|
+
'not-a-bundle',
|
|
11
|
+
'not-a-workbench-module',
|
|
12
|
+
'network',
|
|
13
|
+
'unknown',
|
|
14
|
+
];
|
|
15
|
+
const SPEC_KINDS = ['registry', 'path', 'git', 'tarball'];
|
|
16
|
+
const FAILURE_KINDS = [
|
|
17
|
+
'pnpm-missing',
|
|
18
|
+
'timeout',
|
|
19
|
+
'not-found',
|
|
20
|
+
'no-matching-version',
|
|
21
|
+
'network',
|
|
22
|
+
'disk-full',
|
|
23
|
+
'permission',
|
|
24
|
+
'build-blocked',
|
|
25
|
+
'integrity',
|
|
26
|
+
'unknown',
|
|
27
|
+
];
|
|
28
|
+
const CANCELLATIONS = ['cancelled', 'too-late', 'not-running'];
|
|
29
|
+
function asRecord(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
31
|
+
}
|
|
32
|
+
function asText(value) {
|
|
33
|
+
return typeof value === 'string' ? value : '';
|
|
34
|
+
}
|
|
35
|
+
function pick(allowed, value) {
|
|
36
|
+
return typeof value === 'string' && allowed.includes(value) ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
function asRegistry(value) {
|
|
39
|
+
if (value === null)
|
|
40
|
+
return null;
|
|
41
|
+
return typeof value === 'string' ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
function asTextArray(value) {
|
|
44
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
45
|
+
}
|
|
46
|
+
function asRegistries(raw) {
|
|
47
|
+
const record = asRecord(raw);
|
|
48
|
+
if (record === undefined)
|
|
49
|
+
return null;
|
|
50
|
+
const registry = asRegistry(record.registry);
|
|
51
|
+
if (registry === undefined)
|
|
52
|
+
return null;
|
|
53
|
+
return {
|
|
54
|
+
registry,
|
|
55
|
+
fallbackRegistries: asTextArray(record.fallbackRegistries),
|
|
56
|
+
resolved: typeof record.resolved === 'string' ? record.resolved : null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function asPackageResult(raw) {
|
|
60
|
+
const record = asRecord(raw);
|
|
61
|
+
if (record === undefined)
|
|
62
|
+
return undefined;
|
|
63
|
+
const kind = pick(FAILURE_KINDS, record.kind);
|
|
64
|
+
return {
|
|
65
|
+
exitCode: typeof record.exitCode === 'number' ? record.exitCode : -1,
|
|
66
|
+
output: asText(record.output),
|
|
67
|
+
truncated: record.truncated === true,
|
|
68
|
+
logPath: asText(record.logPath),
|
|
69
|
+
...(kind === undefined ? {} : { kind }),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function asChange(raw) {
|
|
73
|
+
const record = asRecord(raw);
|
|
74
|
+
if (record === undefined)
|
|
75
|
+
return null;
|
|
76
|
+
const application = pick(APPLICATIONS, record.application);
|
|
77
|
+
const stage = pick(STAGES, record.stage);
|
|
78
|
+
if (application === undefined || stage === undefined)
|
|
79
|
+
return null;
|
|
80
|
+
const error = asRecord(record.error);
|
|
81
|
+
const packageResult = asPackageResult(record.packageResult);
|
|
82
|
+
const pendingBuilds = asTextArray(record.pendingBuilds);
|
|
83
|
+
const restartReason = pick(RESTART_REASONS, record.restartReason);
|
|
84
|
+
const failedAt = pick(['registry', 'spec-host'], record.failedAt);
|
|
85
|
+
const bundle = asText(record.bundle);
|
|
86
|
+
const version = asText(record.version);
|
|
87
|
+
const errorCode = error === undefined ? '' : asText(error.code);
|
|
88
|
+
const errorDetail = error === undefined ? '' : asText(error.diagnostic);
|
|
89
|
+
return {
|
|
90
|
+
application,
|
|
91
|
+
stage,
|
|
92
|
+
changed: record.changed === true,
|
|
93
|
+
target: asText(record.target),
|
|
94
|
+
...(bundle === '' ? {} : { bundle }),
|
|
95
|
+
...(version === '' ? {} : { version }),
|
|
96
|
+
...(errorCode === '' ? {} : { errorCode }),
|
|
97
|
+
...(errorDetail === '' ? {} : { errorDetail }),
|
|
98
|
+
...(packageResult === undefined ? {} : { packageResult }),
|
|
99
|
+
...(pendingBuilds.length === 0 ? {} : { pendingBuilds }),
|
|
100
|
+
...(restartReason === undefined ? {} : { restartReason }),
|
|
101
|
+
...(failedAt === undefined ? {} : { failedAt }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function asInspection(raw) {
|
|
105
|
+
const record = asRecord(raw);
|
|
106
|
+
if (record === undefined)
|
|
107
|
+
return null;
|
|
108
|
+
const status = asText(record.status);
|
|
109
|
+
if (status === 'refused') {
|
|
110
|
+
const problem = pick(PROBLEMS, record.problem) ?? 'unknown';
|
|
111
|
+
const registries = Array.isArray(record.registries)
|
|
112
|
+
? record.registries.map(asRegistry).filter((item) => item !== undefined)
|
|
113
|
+
: [];
|
|
114
|
+
return {
|
|
115
|
+
status: 'refused',
|
|
116
|
+
problem,
|
|
117
|
+
reason: asText(record.reason),
|
|
118
|
+
...(registries.length === 0 ? {} : { registries }),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (status !== 'accepted')
|
|
122
|
+
return null;
|
|
123
|
+
const kind = pick(SPEC_KINDS, record.kind);
|
|
124
|
+
const registry = asRegistry(record.registry);
|
|
125
|
+
if (kind === undefined || registry === undefined)
|
|
126
|
+
return null;
|
|
127
|
+
const name = asText(record.name);
|
|
128
|
+
const version = asText(record.version);
|
|
129
|
+
const description = asText(record.description);
|
|
130
|
+
const host = asText(record.host);
|
|
131
|
+
return {
|
|
132
|
+
status: 'accepted',
|
|
133
|
+
kind,
|
|
134
|
+
bundle: typeof record.bundle === 'boolean' ? record.bundle : null,
|
|
135
|
+
registry,
|
|
136
|
+
...(name === '' ? {} : { name }),
|
|
137
|
+
...(version === '' ? {} : { version }),
|
|
138
|
+
...(description === '' ? {} : { description }),
|
|
139
|
+
...(host === '' ? {} : { host }),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function readBundleManager(ctx) {
|
|
143
|
+
const face = ctx.get(MANAGER_SERVICE);
|
|
144
|
+
if (face === undefined)
|
|
145
|
+
return undefined;
|
|
146
|
+
if (typeof face.installBundle !== 'function' || typeof face.removeBundle !== 'function')
|
|
147
|
+
return undefined;
|
|
148
|
+
return {
|
|
149
|
+
async registries() {
|
|
150
|
+
if (typeof face.registries !== 'function')
|
|
151
|
+
return null;
|
|
152
|
+
return asRegistries(await face.registries());
|
|
153
|
+
},
|
|
154
|
+
async inspect(spec, registry) {
|
|
155
|
+
if (typeof face.inspect !== 'function')
|
|
156
|
+
return null;
|
|
157
|
+
return asInspection(await face.inspect(spec, registry === undefined ? undefined : { registry }));
|
|
158
|
+
},
|
|
159
|
+
async install(request) {
|
|
160
|
+
const options = {
|
|
161
|
+
...(request.registry === undefined ? {} : { registry: request.registry }),
|
|
162
|
+
...(request.requestId === undefined ? {} : { requestId: request.requestId }),
|
|
163
|
+
...(request.approvedBuilds === undefined ? {} : { approvedBuilds: request.approvedBuilds }),
|
|
164
|
+
};
|
|
165
|
+
return asChange(await face.installBundle?.(request.spec, options));
|
|
166
|
+
},
|
|
167
|
+
async remove(name) {
|
|
168
|
+
return asChange(await face.removeBundle?.(name));
|
|
169
|
+
},
|
|
170
|
+
async setEnabled(name, enabled) {
|
|
171
|
+
if (typeof face.setBundleEnabled !== 'function')
|
|
172
|
+
return null;
|
|
173
|
+
return asChange(await face.setBundleEnabled(name, enabled));
|
|
174
|
+
},
|
|
175
|
+
async cancel(requestId) {
|
|
176
|
+
if (typeof face.cancelInstall !== 'function')
|
|
177
|
+
return null;
|
|
178
|
+
return pick(CANCELLATIONS, (asRecord(await face.cancelInstall(requestId)) ?? {}).status) ?? null;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
+
import type { RouteHandler } from '@dsh-workbench/shared/host';
|
|
3
|
+
import type { DshPluginsSnapshot } from '@dsh-workbench/shared/contract/protocol/dsh-plugins';
|
|
4
|
+
export declare function readDshPlugins(ctx: DshHostContext): Promise<DshPluginsSnapshot>;
|
|
5
|
+
export declare function dshPluginsHandler(ctx: DshHostContext): RouteHandler;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const MANAGER_SERVICE = 'pluginManager';
|
|
2
|
+
function asRecord(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
function asArray(value) {
|
|
6
|
+
if (Array.isArray(value))
|
|
7
|
+
return value;
|
|
8
|
+
const record = asRecord(value);
|
|
9
|
+
if (record === undefined)
|
|
10
|
+
return [];
|
|
11
|
+
for (const key of ['rows', 'items', 'list', 'bundles', 'plugins']) {
|
|
12
|
+
const inner = record[key];
|
|
13
|
+
if (Array.isArray(inner))
|
|
14
|
+
return inner;
|
|
15
|
+
}
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
function asText(value) {
|
|
19
|
+
return typeof value === 'string' ? value : '';
|
|
20
|
+
}
|
|
21
|
+
function rowOf(value) {
|
|
22
|
+
const record = asRecord(value);
|
|
23
|
+
if (record === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
const name = asText(record.name) || asText(record.id);
|
|
26
|
+
if (name === '')
|
|
27
|
+
return undefined;
|
|
28
|
+
return { name, version: asText(record.version), enabled: record.disabled !== true && record.enabled !== false };
|
|
29
|
+
}
|
|
30
|
+
function rowsOf(value) {
|
|
31
|
+
const rows = [];
|
|
32
|
+
for (const item of asArray(value)) {
|
|
33
|
+
const row = rowOf(item);
|
|
34
|
+
if (row !== undefined)
|
|
35
|
+
rows.push(row);
|
|
36
|
+
}
|
|
37
|
+
return rows;
|
|
38
|
+
}
|
|
39
|
+
export async function readDshPlugins(ctx) {
|
|
40
|
+
const manager = ctx.get(MANAGER_SERVICE);
|
|
41
|
+
if (manager === undefined)
|
|
42
|
+
return { available: false, bundles: [], plugins: [] };
|
|
43
|
+
const [bundles, plugins] = await Promise.all([
|
|
44
|
+
manager.listBundles === undefined ? undefined : manager.listBundles(),
|
|
45
|
+
manager.listPlugins === undefined ? undefined : manager.listPlugins(),
|
|
46
|
+
]);
|
|
47
|
+
return { available: true, bundles: rowsOf(bundles), plugins: rowsOf(plugins) };
|
|
48
|
+
}
|
|
49
|
+
export function dshPluginsHandler(ctx) {
|
|
50
|
+
return () => readDshPlugins(ctx);
|
|
51
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { DshHostContext } from '@dsh-workbench/shared';
|
|
2
|
+
export interface UnitDeclarationSource {
|
|
3
|
+
readonly pkg: string;
|
|
4
|
+
readonly declaration: unknown;
|
|
5
|
+
}
|
|
6
|
+
export type InstalledModule = {
|
|
7
|
+
readonly kind: 'module';
|
|
8
|
+
readonly pkg: string;
|
|
9
|
+
readonly unit: string;
|
|
10
|
+
readonly version: string;
|
|
11
|
+
readonly registry?: string;
|
|
12
|
+
readonly local?: boolean;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: 'not-module';
|
|
15
|
+
readonly pkg: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'unknown';
|
|
18
|
+
readonly pkg: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function isPackageName(value: string): boolean;
|
|
21
|
+
export declare function profileDirOf(ctx: DshHostContext): string | null;
|
|
22
|
+
export interface ProfileModule {
|
|
23
|
+
readonly pkg: string;
|
|
24
|
+
readonly unit: string;
|
|
25
|
+
readonly version: string;
|
|
26
|
+
readonly registry?: string;
|
|
27
|
+
readonly local?: boolean;
|
|
28
|
+
readonly fromRegistry?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function profileManifestOf(profileDir: string | null): Readonly<Record<string, unknown>> | undefined;
|
|
31
|
+
export declare function profileModules(profileDir: string | null): readonly ProfileModule[];
|
|
32
|
+
export declare function createDeclarationLookup(profileDir: string | null): (unit: string) => UnitDeclarationSource | undefined;
|
|
33
|
+
export declare function moduleDeclarationOf(profileDir: string | null, pkg: string): unknown;
|
|
34
|
+
export interface DeclarationGap {
|
|
35
|
+
readonly pkg: string;
|
|
36
|
+
readonly problems: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function declarationGaps(profileDir: string | null): readonly DeclarationGap[] | undefined;
|
|
39
|
+
export declare function declaredPluginCommands(profileDir: string | null): ReadonlyMap<string, ReadonlySet<string>>;
|
|
40
|
+
export declare function readInstalledModule(profileDir: string | null, pkg: string): InstalledModule;
|
|
41
|
+
export declare function isPackedArtifactSpec(spec: string): boolean;
|
|
42
|
+
export declare function isLocalDependencySpec(spec: string): boolean;
|
|
43
|
+
export declare function isRegistryDependencySpec(spec: string): boolean;
|
|
44
|
+
export interface ProfilePackage {
|
|
45
|
+
readonly pkg: string;
|
|
46
|
+
readonly version: string;
|
|
47
|
+
readonly registry?: string;
|
|
48
|
+
readonly local?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export declare function readProfilePackage(profileDir: string | null, pkg: string): ProfilePackage | undefined;
|
|
51
|
+
export declare function packageVersionAt(packageRoot: string): string | undefined;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { declarationProblemsOf, packageSourceOf, readDeclaration, scopeOf } from '@dsh-workbench/shared/host';
|
|
4
|
+
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
5
|
+
function asRecord(value) {
|
|
6
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
7
|
+
}
|
|
8
|
+
function asText(value) {
|
|
9
|
+
return typeof value === 'string' ? value : '';
|
|
10
|
+
}
|
|
11
|
+
export function isPackageName(value) {
|
|
12
|
+
return value.length <= 214 && PACKAGE_NAME.test(value);
|
|
13
|
+
}
|
|
14
|
+
export function profileDirOf(ctx) {
|
|
15
|
+
const profile = ctx.get('profileContext');
|
|
16
|
+
const dir = typeof profile?.dir === 'string' ? profile.dir.trim() : '';
|
|
17
|
+
return dir === '' ? null : dir;
|
|
18
|
+
}
|
|
19
|
+
function stringsOf(value) {
|
|
20
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
21
|
+
}
|
|
22
|
+
export function profileManifestOf(profileDir) {
|
|
23
|
+
if (profileDir === null)
|
|
24
|
+
return undefined;
|
|
25
|
+
try {
|
|
26
|
+
return asRecord(JSON.parse(fs.readFileSync(path.join(profileDir, 'package.json'), 'utf8')));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function profileModules(profileDir) {
|
|
33
|
+
const record = profileManifestOf(profileDir);
|
|
34
|
+
if (record === undefined)
|
|
35
|
+
return [];
|
|
36
|
+
const bundles = stringsOf(asRecord(asRecord(record.dsh)?.profile)?.bundles);
|
|
37
|
+
const dependencies = asRecord(record.dependencies) ?? {};
|
|
38
|
+
const names = bundles.length > 0 ? bundles : Object.keys(dependencies);
|
|
39
|
+
const found = [];
|
|
40
|
+
for (const pkg of names) {
|
|
41
|
+
const read = readInstalledModule(profileDir, pkg);
|
|
42
|
+
if (read.kind === 'module') {
|
|
43
|
+
const spec = asText(dependencies[read.pkg]);
|
|
44
|
+
found.push({
|
|
45
|
+
pkg: read.pkg,
|
|
46
|
+
unit: read.unit,
|
|
47
|
+
version: read.version,
|
|
48
|
+
...(read.registry === undefined ? {} : { registry: read.registry }),
|
|
49
|
+
local: isLocalDependencySpec(spec),
|
|
50
|
+
fromRegistry: isRegistryDependencySpec(spec),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return found;
|
|
55
|
+
}
|
|
56
|
+
export function createDeclarationLookup(profileDir) {
|
|
57
|
+
let roster;
|
|
58
|
+
const scan = () => new Map(profileModules(profileDir).map((row) => [row.unit, row.pkg]));
|
|
59
|
+
return (unit) => {
|
|
60
|
+
roster ??= scan();
|
|
61
|
+
let pkg = roster.get(unit);
|
|
62
|
+
if (pkg === undefined) {
|
|
63
|
+
roster = scan();
|
|
64
|
+
pkg = roster.get(unit);
|
|
65
|
+
}
|
|
66
|
+
return pkg === undefined ? undefined : { pkg, declaration: moduleDeclarationOf(profileDir, pkg) };
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function registryFromLockfile(profileDir, pkg) {
|
|
70
|
+
let text;
|
|
71
|
+
try {
|
|
72
|
+
text = fs.readFileSync(path.join(profileDir, 'pnpm-lock.yaml'), 'utf8');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const at = text.indexOf(` ${pkg}@`);
|
|
78
|
+
const quoted = at === -1 ? text.indexOf(` '${pkg}@`) : at;
|
|
79
|
+
if (quoted === -1)
|
|
80
|
+
return undefined;
|
|
81
|
+
const headerEnd = text.indexOf('\n', quoted);
|
|
82
|
+
const end = headerEnd === -1 ? -1 : text.indexOf('\n ', headerEnd + 1);
|
|
83
|
+
const block = text.slice(quoted, end === -1 ? quoted + 900 : end);
|
|
84
|
+
const match = /tarball:\s*([^\s,}]+)/.exec(block);
|
|
85
|
+
const tarball = match?.[1];
|
|
86
|
+
if (tarball === undefined || !tarball.startsWith('http'))
|
|
87
|
+
return undefined;
|
|
88
|
+
const cut = tarball.lastIndexOf(`/${pkg}/`);
|
|
89
|
+
if (cut <= 'https://'.length)
|
|
90
|
+
return undefined;
|
|
91
|
+
return tarball.slice(0, cut);
|
|
92
|
+
}
|
|
93
|
+
function unquoted(value) {
|
|
94
|
+
const quote = value.charAt(0);
|
|
95
|
+
if (value.length >= 2 && (quote === '"' || quote === "'") && value.endsWith(quote)) {
|
|
96
|
+
return value.slice(1, -1).trim();
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function registryFromNpmrc(profileDir, pkg) {
|
|
101
|
+
let text;
|
|
102
|
+
try {
|
|
103
|
+
text = fs.readFileSync(path.join(profileDir, '.npmrc'), 'utf8');
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const scope = scopeOf(pkg);
|
|
109
|
+
let fallback;
|
|
110
|
+
for (const line of text.split('\n')) {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
if (trimmed === '' || trimmed.startsWith('#') || trimmed.startsWith(';'))
|
|
113
|
+
continue;
|
|
114
|
+
const separator = trimmed.indexOf('=');
|
|
115
|
+
if (separator <= 0)
|
|
116
|
+
continue;
|
|
117
|
+
const key = trimmed.slice(0, separator).trim();
|
|
118
|
+
const value = unquoted(trimmed.slice(separator + 1).trim());
|
|
119
|
+
if (value === '')
|
|
120
|
+
continue;
|
|
121
|
+
if (scope !== null && key === `${scope}:registry`)
|
|
122
|
+
return value;
|
|
123
|
+
if (key === 'registry')
|
|
124
|
+
fallback = value;
|
|
125
|
+
}
|
|
126
|
+
return fallback;
|
|
127
|
+
}
|
|
128
|
+
function registryOf(profileDir, pkg, manifest) {
|
|
129
|
+
const declared = asText(asRecord(manifest.publishConfig)?.registry);
|
|
130
|
+
if (declared !== '')
|
|
131
|
+
return declared;
|
|
132
|
+
const recorded = packageSourceOf(profileDir, pkg);
|
|
133
|
+
if (recorded !== undefined)
|
|
134
|
+
return recorded.registry;
|
|
135
|
+
const locked = registryFromLockfile(profileDir, pkg);
|
|
136
|
+
return locked === undefined ? registryFromNpmrc(profileDir, pkg) : locked;
|
|
137
|
+
}
|
|
138
|
+
export function moduleDeclarationOf(profileDir, pkg) {
|
|
139
|
+
if (profileDir === null || !isPackageName(pkg))
|
|
140
|
+
return undefined;
|
|
141
|
+
try {
|
|
142
|
+
const text = fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8');
|
|
143
|
+
return asRecord(asRecord(asRecord(JSON.parse(text))?.dsh)?.workbench);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function declarationGaps(profileDir) {
|
|
150
|
+
if (profileManifestOf(profileDir) === undefined)
|
|
151
|
+
return undefined;
|
|
152
|
+
const gaps = [];
|
|
153
|
+
for (const module of profileModules(profileDir)) {
|
|
154
|
+
const problems = declarationProblemsOf(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
155
|
+
if (problems.length > 0)
|
|
156
|
+
gaps.push({ pkg: module.pkg, problems });
|
|
157
|
+
}
|
|
158
|
+
return gaps;
|
|
159
|
+
}
|
|
160
|
+
export function declaredPluginCommands(profileDir) {
|
|
161
|
+
const declared = new Map();
|
|
162
|
+
for (const module of profileModules(profileDir)) {
|
|
163
|
+
const read = readDeclaration(module.pkg, moduleDeclarationOf(profileDir, module.pkg));
|
|
164
|
+
for (const need of read.manifest?.needs ?? []) {
|
|
165
|
+
if (need.kind !== 'extension')
|
|
166
|
+
continue;
|
|
167
|
+
const name = need.name.trim();
|
|
168
|
+
if (name === '')
|
|
169
|
+
continue;
|
|
170
|
+
const commands = declared.get(name) ?? new Set();
|
|
171
|
+
for (const command of need.commands ?? [])
|
|
172
|
+
commands.add(command);
|
|
173
|
+
declared.set(name, commands);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return declared;
|
|
177
|
+
}
|
|
178
|
+
export function readInstalledModule(profileDir, pkg) {
|
|
179
|
+
if (profileDir === null || !isPackageName(pkg))
|
|
180
|
+
return { kind: 'unknown', pkg };
|
|
181
|
+
let manifest;
|
|
182
|
+
try {
|
|
183
|
+
manifest = JSON.parse(fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8'));
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return { kind: 'unknown', pkg };
|
|
187
|
+
}
|
|
188
|
+
const record = asRecord(manifest);
|
|
189
|
+
if (record === undefined)
|
|
190
|
+
return { kind: 'unknown', pkg };
|
|
191
|
+
const declared = asRecord(asRecord(record.dsh)?.workbench);
|
|
192
|
+
const unit = declared === undefined ? '' : asText(declared.unit);
|
|
193
|
+
if (unit === '')
|
|
194
|
+
return { kind: 'not-module', pkg };
|
|
195
|
+
const registry = registryOf(profileDir, pkg, record);
|
|
196
|
+
return {
|
|
197
|
+
kind: 'module',
|
|
198
|
+
pkg,
|
|
199
|
+
unit,
|
|
200
|
+
version: asText(record.version),
|
|
201
|
+
...(registry === undefined ? {} : { registry }),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const PACKED_ARTIFACT = /\.(?:tgz|tar\.gz|tar)$/i;
|
|
205
|
+
export function isPackedArtifactSpec(spec) {
|
|
206
|
+
const trimmed = spec.trim();
|
|
207
|
+
const pkgPath = trimmed.startsWith('file:') ? trimmed.slice('file:'.length) : trimmed;
|
|
208
|
+
return PACKED_ARTIFACT.test(pkgPath);
|
|
209
|
+
}
|
|
210
|
+
export function isLocalDependencySpec(spec) {
|
|
211
|
+
const trimmed = spec.trim();
|
|
212
|
+
if (isPackedArtifactSpec(trimmed))
|
|
213
|
+
return false;
|
|
214
|
+
return (trimmed.startsWith('link:') ||
|
|
215
|
+
trimmed.startsWith('file:') ||
|
|
216
|
+
trimmed.startsWith('./') ||
|
|
217
|
+
trimmed.startsWith('../') ||
|
|
218
|
+
trimmed.startsWith('/'));
|
|
219
|
+
}
|
|
220
|
+
const NON_REGISTRY_SPEC = /^(?:git\+|git:|github:|gitlab:|bitbucket:|https?:|ssh:|workspace:)/i;
|
|
221
|
+
export function isRegistryDependencySpec(spec) {
|
|
222
|
+
const trimmed = spec.trim();
|
|
223
|
+
if (trimmed === '')
|
|
224
|
+
return true;
|
|
225
|
+
if (isLocalDependencySpec(trimmed) || isPackedArtifactSpec(trimmed))
|
|
226
|
+
return false;
|
|
227
|
+
return !NON_REGISTRY_SPEC.test(trimmed);
|
|
228
|
+
}
|
|
229
|
+
export function readProfilePackage(profileDir, pkg) {
|
|
230
|
+
if (profileDir === null || !isPackageName(pkg))
|
|
231
|
+
return undefined;
|
|
232
|
+
let manifest;
|
|
233
|
+
try {
|
|
234
|
+
manifest = JSON.parse(fs.readFileSync(path.join(profileDir, 'node_modules', pkg, 'package.json'), 'utf8'));
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
const record = asRecord(manifest);
|
|
240
|
+
if (record === undefined)
|
|
241
|
+
return undefined;
|
|
242
|
+
const version = asText(record.version);
|
|
243
|
+
if (version === '')
|
|
244
|
+
return undefined;
|
|
245
|
+
const registry = registryOf(profileDir, pkg, record);
|
|
246
|
+
const dependencies = asRecord(profileManifestOf(profileDir)?.dependencies) ?? {};
|
|
247
|
+
const local = isLocalDependencySpec(asText(dependencies[pkg]));
|
|
248
|
+
return {
|
|
249
|
+
pkg,
|
|
250
|
+
version,
|
|
251
|
+
...(registry === undefined ? {} : { registry }),
|
|
252
|
+
...(local ? { local: true } : {}),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
export function packageVersionAt(packageRoot) {
|
|
256
|
+
let manifest;
|
|
257
|
+
try {
|
|
258
|
+
manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
const version = asText(asRecord(manifest)?.version);
|
|
264
|
+
return version === '' ? undefined : version;
|
|
265
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DshShell } from '@dsh-workbench/shared';
|
|
2
|
+
export declare function withPackageDir<T>(options: {
|
|
3
|
+
readonly shell?: DshShell | undefined;
|
|
4
|
+
readonly spec: string;
|
|
5
|
+
readonly registry?: string | undefined;
|
|
6
|
+
}, visit: (dir: string) => T): Promise<T | undefined>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { npmCacheDir } from '@dsh-workbench/adapter-runtime';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { INSTALL_TIMEOUT_MS, parseJsonLoose, quoteShellArg, runShell } from '@dsh-workbench/shared/host';
|
|
6
|
+
import { splitNpmSpec } from './package-kind.js';
|
|
7
|
+
import { localDirOf } from './registry-declaration.js';
|
|
8
|
+
function packedFileFrom(stdout, root) {
|
|
9
|
+
const parsed = parseJsonLoose(stdout);
|
|
10
|
+
if (Array.isArray(parsed)) {
|
|
11
|
+
const first = parsed[0];
|
|
12
|
+
const filename = typeof first === 'object' && first !== null ? first.filename : undefined;
|
|
13
|
+
if (typeof filename === 'string' && filename.trim() !== '') {
|
|
14
|
+
const at = path.join(root, path.basename(filename.trim()));
|
|
15
|
+
if (fs.existsSync(at))
|
|
16
|
+
return at;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const found = fs.readdirSync(root).filter((name) => name.endsWith('.tgz'));
|
|
21
|
+
return found.length === 1 ? path.join(root, found[0] ?? '') : undefined;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function withPackageDir(options, visit) {
|
|
28
|
+
const local = localDirOf(options.spec);
|
|
29
|
+
if (local !== undefined)
|
|
30
|
+
return visit(local);
|
|
31
|
+
if (splitNpmSpec(options.spec) === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
const shell = options.shell;
|
|
34
|
+
if (shell === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
let root;
|
|
37
|
+
try {
|
|
38
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'wb-package-'));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const at = options.registry === undefined ? '' : ` --registry ${quoteShellArg(options.registry)}`;
|
|
45
|
+
const packed = await runShell(shell, `npm pack ${quoteShellArg(options.spec)} --ignore-scripts --json --pack-destination ${quoteShellArg(root)} --cache ${quoteShellArg(npmCacheDir())}${at}`, { timeoutMs: INSTALL_TIMEOUT_MS }).catch(() => undefined);
|
|
46
|
+
if (packed === undefined || packed.exitCode !== 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
const file = packedFileFrom(packed.stdout, root);
|
|
49
|
+
if (file === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
const unpacked = await runShell(shell, `tar -xzf ${quoteShellArg(file)} -C ${quoteShellArg(root)}`, {
|
|
52
|
+
timeoutMs: INSTALL_TIMEOUT_MS,
|
|
53
|
+
}).catch(() => undefined);
|
|
54
|
+
if (unpacked === undefined || unpacked.exitCode !== 0)
|
|
55
|
+
return undefined;
|
|
56
|
+
const dir = path.join(root, 'package');
|
|
57
|
+
if (!fs.existsSync(dir))
|
|
58
|
+
return undefined;
|
|
59
|
+
return visit(dir);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type PackageKind = 'runtime-plugin' | 'dsh-bundle' | 'both' | 'neither';
|
|
2
|
+
export interface PackageVerdict {
|
|
3
|
+
readonly kind: PackageKind;
|
|
4
|
+
readonly pkg?: string;
|
|
5
|
+
readonly pluginName?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface NpmRef {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly version?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const OPENCLI_MANIFEST_FILE = "opencli-plugin.json";
|
|
12
|
+
export declare function splitNpmSpec(spec: string): NpmRef | undefined;
|
|
13
|
+
export declare function isCommandFile(name: string): boolean;
|
|
14
|
+
export declare function pluginManifestOf(dir: string): Readonly<Record<string, unknown>> | undefined;
|
|
15
|
+
export declare function hasCommandFile(dir: string): boolean;
|
|
16
|
+
export declare function pluginAt(dir: string): {
|
|
17
|
+
readonly name?: string;
|
|
18
|
+
} | undefined;
|
|
19
|
+
export declare function dshMarkersOf(raw: unknown): boolean;
|
|
20
|
+
export declare function verdictOf(dir: string): PackageVerdict | undefined;
|
|
21
|
+
export declare function refusalReason(): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { isPackageName } from '@dsh-workbench/shared/host';
|
|
4
|
+
import { dshRecordOf, packageManifestOf, packageNameOf } from './registry-declaration.js';
|
|
5
|
+
export const OPENCLI_MANIFEST_FILE = 'opencli-plugin.json';
|
|
6
|
+
const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]*$/;
|
|
7
|
+
export function splitNpmSpec(spec) {
|
|
8
|
+
const value = spec.trim();
|
|
9
|
+
const at = value.lastIndexOf('@');
|
|
10
|
+
const name = at > 0 ? value.slice(0, at) : value;
|
|
11
|
+
const version = at > 0 ? value.slice(at + 1) : '';
|
|
12
|
+
if (!isPackageName(name))
|
|
13
|
+
return undefined;
|
|
14
|
+
if (version !== '' && !VERSION.test(version))
|
|
15
|
+
return undefined;
|
|
16
|
+
return { name, ...(version === '' ? {} : { version }) };
|
|
17
|
+
}
|
|
18
|
+
function asRecord(value) {
|
|
19
|
+
return typeof value === 'object' && value !== null ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function isCommandFile(name) {
|
|
22
|
+
if (name.endsWith('.d.ts') || name.endsWith('.test.ts'))
|
|
23
|
+
return false;
|
|
24
|
+
if (name.endsWith('.d.js'))
|
|
25
|
+
return false;
|
|
26
|
+
return name.endsWith('.ts') || name.endsWith('.js');
|
|
27
|
+
}
|
|
28
|
+
export function pluginManifestOf(dir) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, OPENCLI_MANIFEST_FILE), 'utf8'));
|
|
31
|
+
if (Array.isArray(parsed))
|
|
32
|
+
return undefined;
|
|
33
|
+
return asRecord(parsed);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function hasCommandFile(dir) {
|
|
40
|
+
try {
|
|
41
|
+
return fs.readdirSync(dir).some(isCommandFile);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function pluginAt(dir) {
|
|
48
|
+
const manifest = pluginManifestOf(dir);
|
|
49
|
+
if (manifest === undefined || !hasCommandFile(dir))
|
|
50
|
+
return undefined;
|
|
51
|
+
const name = manifest.name;
|
|
52
|
+
return typeof name === 'string' && name.trim() !== '' ? { name: name.trim() } : {};
|
|
53
|
+
}
|
|
54
|
+
export function dshMarkersOf(raw) {
|
|
55
|
+
const record = asRecord(raw);
|
|
56
|
+
return record !== undefined && ('bundle' in record || 'workbench' in record);
|
|
57
|
+
}
|
|
58
|
+
export function verdictOf(dir) {
|
|
59
|
+
const plugin = pluginAt(dir);
|
|
60
|
+
if (packageManifestOf(dir) === undefined && plugin === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
const dsh = dshMarkersOf(dshRecordOf(dir));
|
|
63
|
+
const kind = plugin !== undefined && dsh ? 'both' : plugin !== undefined ? 'runtime-plugin' : dsh ? 'dsh-bundle' : 'neither';
|
|
64
|
+
const pkg = packageNameOf(dir);
|
|
65
|
+
return {
|
|
66
|
+
kind,
|
|
67
|
+
...(pkg === undefined ? {} : { pkg }),
|
|
68
|
+
...(plugin?.name === undefined ? {} : { pluginName: plugin.name }),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function refusalReason() {
|
|
72
|
+
return '这个包既不是工作台的模块,也不是 opencli 的插件,装了也用不上';
|
|
73
|
+
}
|
package/dist/package-route.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { PackageRoute } from '@dsh-workbench/shared/host';
|
|
1
|
+
import type { ResolvedPluginSource } from '@dsh-workbench/shared';
|
|
2
|
+
import type { PackageRoute, Seam } from '@dsh-workbench/shared/host';
|
|
3
3
|
export declare const LOCAL_PLUGIN_ENV = "DSH_WB_OPENCLI_PLUGIN_PATH";
|
|
4
4
|
export declare const PLUGIN_REGISTRY_ENV = "DSH_WB_OPENCLI_PLUGIN_REGISTRY";
|
|
5
5
|
export declare function packageRouteOf(options: {
|
|
6
6
|
readonly spec: string;
|
|
7
|
-
readonly
|
|
7
|
+
readonly seam?: Seam | undefined;
|
|
8
8
|
readonly registry?: string | undefined;
|
|
9
9
|
readonly force?: boolean;
|
|
10
10
|
}): Promise<PackageRoute | undefined>;
|
package/dist/package-route.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { createPackageRouter, localDirOf, splitNpmSpec } from '@dsh-workbench/shared/host';
|
|
2
|
-
import {
|
|
2
|
+
import { OPENCLI_MANIFEST_FILE } from '@dsh-workbench/adapter-runtime';
|
|
3
3
|
import { registryDshOf } from './registry-declaration.js';
|
|
4
4
|
import { resolvePluginSource } from './plugin-source.js';
|
|
5
5
|
export const LOCAL_PLUGIN_ENV = 'DSH_WB_OPENCLI_PLUGIN_PATH';
|
|
6
6
|
export const PLUGIN_REGISTRY_ENV = 'DSH_WB_OPENCLI_PLUGIN_REGISTRY';
|
|
7
7
|
const router = createPackageRouter({
|
|
8
8
|
manifestFile: OPENCLI_MANIFEST_FILE,
|
|
9
|
-
cacheDir: npmCacheDir(),
|
|
10
9
|
dshLookup: (options) => registryDshOf(options),
|
|
11
10
|
});
|
|
12
11
|
export async function packageRouteOf(options) {
|
package/dist/plugin-catalog.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Seam } from '@dsh-workbench/shared/host';
|
|
2
2
|
export interface PluginCommandArg {
|
|
3
3
|
readonly name: string;
|
|
4
4
|
readonly type: string;
|
|
@@ -52,7 +52,7 @@ export interface PluginCatalog {
|
|
|
52
52
|
snapshot(): PluginCatalogSnapshot;
|
|
53
53
|
}
|
|
54
54
|
export interface PluginCatalogOptions {
|
|
55
|
-
readonly
|
|
55
|
+
readonly seam?: Seam | undefined;
|
|
56
56
|
readonly command: string;
|
|
57
57
|
readonly timeoutMs?: number;
|
|
58
58
|
}
|
package/dist/plugin-catalog.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OPENCLI_TIMEOUT_MS, parseJsonLoose,
|
|
1
|
+
import { OPENCLI_TIMEOUT_MS, now, parseJsonLoose, productWrite } from '@dsh-workbench/shared/host';
|
|
2
2
|
function recordOf(value) {
|
|
3
3
|
return typeof value === 'object' && value !== null ? value : undefined;
|
|
4
4
|
}
|
|
@@ -141,12 +141,12 @@ export function createPluginCatalog(options) {
|
|
|
141
141
|
};
|
|
142
142
|
let heavy;
|
|
143
143
|
async function read(line) {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
return { ok: false, problem: '
|
|
144
|
+
const seam = options.seam;
|
|
145
|
+
if (seam === undefined) {
|
|
146
|
+
return { ok: false, problem: '没有执行缝,探不了这个命令面' };
|
|
147
147
|
}
|
|
148
148
|
try {
|
|
149
|
-
const result = await
|
|
149
|
+
const result = await seam.run({ command: line, intent: productWrite(), retry: 'safe', timeoutMs });
|
|
150
150
|
if (result.timedOut)
|
|
151
151
|
return { ok: false, problem: `${line} 超过 ${String(Math.round(timeoutMs / 1000))} 秒没返回` };
|
|
152
152
|
if (result.exitCode !== 0) {
|
|
@@ -160,7 +160,7 @@ export function createPluginCatalog(options) {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
function fail(problem) {
|
|
163
|
-
current = { ok: false, at:
|
|
163
|
+
current = { ok: false, at: now(), sets: current.sets, problems: [problem] };
|
|
164
164
|
return current;
|
|
165
165
|
}
|
|
166
166
|
async function refresh(callOptions = {}) {
|
|
@@ -173,7 +173,7 @@ export function createPluginCatalog(options) {
|
|
|
173
173
|
}
|
|
174
174
|
const fingerprint = fingerprintOf(plugins);
|
|
175
175
|
if (!callOptions.force && heavy !== undefined && heavy.fingerprint === fingerprint) {
|
|
176
|
-
current = { ok: true, at:
|
|
176
|
+
current = { ok: true, at: now(), sets: heavy.sets, problems: heavy.problems };
|
|
177
177
|
return current;
|
|
178
178
|
}
|
|
179
179
|
const commandsRun = await read(`${options.command} list -f json`);
|
|
@@ -185,7 +185,7 @@ export function createPluginCatalog(options) {
|
|
|
185
185
|
}
|
|
186
186
|
const attributed = attributeCommands(plugins, rows);
|
|
187
187
|
heavy = { fingerprint, sets: attributed.sets, problems: attributed.problems };
|
|
188
|
-
current = { ok: true, at:
|
|
188
|
+
current = { ok: true, at: now(), sets: attributed.sets, problems: attributed.problems };
|
|
189
189
|
return current;
|
|
190
190
|
}
|
|
191
191
|
return { refresh, snapshot: () => current };
|
package/dist/plugin-units.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { now as clockNow } from '@dsh-workbench/shared/host';
|
|
1
2
|
import { PROBE_TTL_MS, TEXT_OUTPUT, jsonText, sessionWorkspace, unitTitleKey } from '@dsh-workbench/shared/host';
|
|
2
3
|
import { pluginLoginHint } from './plugin-login-hint.js';
|
|
3
4
|
import { PLUGIN_PROMPT_BUDGET, RUN_CODE_TOOL, pluginApprovalFor, pluginPromptChars, pluginToolArgv, pluginToolDescription, pluginToolName, pluginToolParameters, pluginToolSession, pluginToolWrites, } from './plugin-tools.js';
|
|
@@ -213,13 +214,13 @@ export function createPluginUnits(options) {
|
|
|
213
214
|
...probed.problems,
|
|
214
215
|
'这次探测没成功:上面这些插件行的工具面维持在上一份(不是"这些插件没有命令")',
|
|
215
216
|
];
|
|
216
|
-
report = { ok: false, at:
|
|
217
|
+
report = { ok: false, at: clockNow(), plugins: report.plugins, problems };
|
|
217
218
|
warn(problems);
|
|
218
219
|
return snapshot();
|
|
219
220
|
}
|
|
220
221
|
const plan = planOf(options, registered);
|
|
221
222
|
reconcile(registered, plan.units, options, note);
|
|
222
|
-
report = { ok: true, at:
|
|
223
|
+
report = { ok: true, at: clockNow(), plugins: plan.rows, problems: plan.problems };
|
|
223
224
|
warn(plan.problems);
|
|
224
225
|
return snapshot();
|
|
225
226
|
}
|
|
@@ -235,7 +236,7 @@ export function createPluginUnits(options) {
|
|
|
235
236
|
return {
|
|
236
237
|
refresh,
|
|
237
238
|
touch() {
|
|
238
|
-
const now =
|
|
239
|
+
const now = clockNow();
|
|
239
240
|
if (inflight !== undefined)
|
|
240
241
|
return;
|
|
241
242
|
if (now - lastTouch < PROBE_TTL_MS)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Seam } from '@dsh-workbench/shared/host';
|
|
2
2
|
export { declarationRefusalOf, dshRecordOf, localDirOf, packageManifestOf, packageNameOf, pathDeclarationOf, } from '@dsh-workbench/shared/host';
|
|
3
3
|
export type { RegistryDshLookup } from '@dsh-workbench/shared/host';
|
|
4
4
|
export declare function registryDshOf(options: {
|
|
5
|
-
|
|
5
|
+
seam: Seam;
|
|
6
6
|
name: string;
|
|
7
7
|
registry?: string | undefined;
|
|
8
8
|
force?: boolean;
|
|
@@ -11,7 +11,7 @@ export declare function registryDshOf(options: {
|
|
|
11
11
|
readonly raw: unknown;
|
|
12
12
|
}>;
|
|
13
13
|
export declare function registryDeclarationOf(options: {
|
|
14
|
-
|
|
14
|
+
seam: Seam;
|
|
15
15
|
name: string;
|
|
16
16
|
registry: string;
|
|
17
17
|
force?: boolean;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { npmCacheDir } from '@dsh-workbench/adapter-runtime';
|
|
2
1
|
import { createRegistryLookup } from '@dsh-workbench/shared/host';
|
|
3
2
|
export { declarationRefusalOf, dshRecordOf, localDirOf, packageManifestOf, packageNameOf, pathDeclarationOf, } from '@dsh-workbench/shared/host';
|
|
4
3
|
const lookup = createRegistryLookup();
|
|
5
4
|
export async function registryDshOf(options) {
|
|
6
|
-
return lookup.registryDshOf(
|
|
5
|
+
return lookup.registryDshOf(options);
|
|
7
6
|
}
|
|
8
7
|
export async function registryDeclarationOf(options) {
|
|
9
|
-
return lookup.registryDeclarationOf(
|
|
8
|
+
return lookup.registryDeclarationOf(options);
|
|
10
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-workbench/adapter-supply",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "Workbench supply adapter package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"directory": "packages/adapter-supply"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@dsh-workbench/adapter-runtime": "1.1.
|
|
26
|
-
"@dsh-workbench/shared": "1.1.
|
|
25
|
+
"@dsh-workbench/adapter-runtime": "1.1.15",
|
|
26
|
+
"@dsh-workbench/shared": "1.1.15"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.19.0",
|