@jini-ai/daemon 0.3.7 → 0.3.8
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/agent-executor.d.ts +8 -2
- package/dist/agent-executor.js +23 -12
- package/dist/run-lifecycle.js +7 -1
- package/package.json +4 -4
- package/dist/artifacts/index.d.ts +0 -14
- package/dist/artifacts/index.js +0 -14
- package/dist/artifacts/manifest.d.ts +0 -90
- package/dist/artifacts/manifest.js +0 -229
- package/dist/artifacts/publication-guard.d.ts +0 -28
- package/dist/artifacts/publication-guard.js +0 -77
- package/dist/artifacts/runtime-compat.d.ts +0 -37
- package/dist/artifacts/runtime-compat.js +0 -33
- package/dist/artifacts/store.d.ts +0 -85
- package/dist/artifacts/store.js +0 -92
- package/dist/artifacts/stub-guard.d.ts +0 -73
- package/dist/artifacts/stub-guard.js +0 -198
- package/dist/artifacts/text-suppression.d.ts +0 -68
- package/dist/artifacts/text-suppression.js +0 -184
package/dist/agent-executor.d.ts
CHANGED
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
import { spawn as nodeSpawn, type ChildProcess } from 'node:child_process';
|
|
93
93
|
import type { Principal } from '@jini-ai/core';
|
|
94
94
|
import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
|
|
95
|
-
import { applyAgentLaunchEnv, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, prepareAgentLogFile, preparePromptFileForAgent, type AcpMcpServerInput, type AcpPermissionHandler, type AgentLaunchResolution, type PreparedAgentLogFile, type PreparedPromptFile, type PromptAugmenter, type RuntimeAgentDef, type RuntimeBuildOptions, type RuntimeContext, type RuntimeLockHold } from '@jini-ai/agent-runtime';
|
|
95
|
+
import { applyAgentLaunchEnv, ensureAgentCapabilities, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, prepareAgentLogFile, preparePromptFileForAgent, type AcpMcpServerInput, type AcpPermissionHandler, type AgentLaunchResolution, type PreparedAgentLogFile, type PreparedPromptFile, type PromptAugmenter, type RuntimeAgentDef, type RuntimeBuildOptions, type RuntimeContext, type RuntimeLockHold } from '@jini-ai/agent-runtime';
|
|
96
96
|
import { collectProcessTreePids, createCommandInvocation, listProcessSnapshots, stopProcesses, type ProcessSnapshot, type StopProcessesResult } from '@jini-ai/platform';
|
|
97
97
|
import type { RunByteJournal } from './continuation/journal.js';
|
|
98
98
|
import { type ImagePromptDelivery } from './image-prompt-delivery.js';
|
|
@@ -336,6 +336,10 @@ export interface AgentExecutorRunInput {
|
|
|
336
336
|
readonly disallowedTools?: readonly string[];
|
|
337
337
|
/** Same mechanism as {@link disallowedTools}, forwarded to `RuntimeBuildOptions.allowedTools`. */
|
|
338
338
|
readonly allowedTools?: readonly string[];
|
|
339
|
+
/** Forwarded verbatim to `RuntimeBuildOptions.settingSources` (an empty list = load none of the CLI's settings layers). */
|
|
340
|
+
readonly settingSources?: readonly string[];
|
|
341
|
+
/** Forwarded verbatim to `RuntimeBuildOptions.settings` (extra settings JSON or file path for this run). */
|
|
342
|
+
readonly settings?: string;
|
|
339
343
|
}
|
|
340
344
|
export interface AgentExecutor {
|
|
341
345
|
/**
|
|
@@ -884,6 +888,8 @@ export interface CreateAgentExecutorOptions {
|
|
|
884
888
|
readonly getAgentDef?: typeof getAgentDef;
|
|
885
889
|
/** @default the real `@jini-ai/agent-runtime` launch resolver */
|
|
886
890
|
readonly resolveAgentLaunch?: typeof resolveAgentLaunch;
|
|
891
|
+
/** @default the real `@jini-ai/agent-runtime` once-per-binary `--help` capability probe */
|
|
892
|
+
readonly ensureAgentCapabilities?: typeof ensureAgentCapabilities;
|
|
887
893
|
/** @default the real `@jini-ai/agent-runtime` PATH-env composer */
|
|
888
894
|
readonly applyAgentLaunchEnv?: typeof applyAgentLaunchEnv;
|
|
889
895
|
/** @default the real `@jini-ai/platform` cross-platform invocation builder */
|
|
@@ -1135,7 +1141,7 @@ export declare function computeChildEnv(spawnEnv: NodeJS.ProcessEnv, mcpBridge:
|
|
|
1135
1141
|
*/
|
|
1136
1142
|
export declare function computeRuntimeContext(preparedPromptFile: PreparedPromptFile | null, preparedLogFile: PreparedAgentLogFile | null, mcpBridge: McpBridgeDelivery | null, resumeSessionId?: string | null, newSessionId?: string): RuntimeContext | undefined;
|
|
1137
1143
|
/** Phase 9a: the def's `buildArgs` 4th argument — `undefined` when the run selects no model/reasoning/permissionMode/overlay/tool-restriction at all (byte-identical to omitting the argument). Pure. */
|
|
1138
|
-
export declare function buildAgentBuildArgsOptions(input: Pick<AgentExecutorRunInput, 'model' | 'reasoning' | 'permissionMode' | 'disallowedTools' | 'allowedTools'>, systemPromptOverlay: string | null | undefined): RuntimeBuildOptions | undefined;
|
|
1144
|
+
export declare function buildAgentBuildArgsOptions(input: Pick<AgentExecutorRunInput, 'model' | 'reasoning' | 'permissionMode' | 'disallowedTools' | 'allowedTools' | 'settingSources' | 'settings'>, systemPromptOverlay: string | null | undefined): RuntimeBuildOptions | undefined;
|
|
1139
1145
|
/**
|
|
1140
1146
|
* {@link resolveSystemPromptOverlayDelivery}'s decision, threaded out of that one resolver to every
|
|
1141
1147
|
* channel that can actually carry the overlay to the CLI.
|
package/dist/agent-executor.js
CHANGED
|
@@ -94,7 +94,7 @@ import { promises as fsPromises } from 'node:fs';
|
|
|
94
94
|
import { homedir, tmpdir } from 'node:os';
|
|
95
95
|
import { join } from 'node:path';
|
|
96
96
|
import { redactSecrets } from '@jini-ai/core';
|
|
97
|
-
import { agentCapabilities, applyAgentLaunchEnv, createClaudeStreamHandler, createCopilotStreamHandler, createJsonEventStreamHandler, createQoderStreamHandler, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
|
|
97
|
+
import { agentCapabilities, applyAgentLaunchEnv, createClaudeStreamHandler, createCopilotStreamHandler, createJsonEventStreamHandler, createQoderStreamHandler, ensureAgentCapabilities, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
|
|
98
98
|
import { collectProcessTreePids, createCommandInvocation, listProcessSnapshots, stopProcesses, } from '@jini-ai/platform';
|
|
99
99
|
import { classifyRunCloseStatus } from './close-status.js';
|
|
100
100
|
import { resolveContinuationTransport } from './continuation/continuation-transport.js';
|
|
@@ -1983,6 +1983,7 @@ function resolveAgentRuntimeDeps(options) {
|
|
|
1983
1983
|
return {
|
|
1984
1984
|
getAgentDef: options.getAgentDef ?? getAgentDef,
|
|
1985
1985
|
resolveAgentLaunch: options.resolveAgentLaunch ?? resolveAgentLaunch,
|
|
1986
|
+
ensureAgentCapabilities: options.ensureAgentCapabilities ?? ensureAgentCapabilities,
|
|
1986
1987
|
applyAgentLaunchEnv: options.applyAgentLaunchEnv ?? applyAgentLaunchEnv,
|
|
1987
1988
|
attachAcpSession: options.attachAcpSession ?? attachAcpSession,
|
|
1988
1989
|
attachPiRpcSession: options.attachPiRpcSession ?? attachPiRpcSession,
|
|
@@ -2189,22 +2190,17 @@ function computeSystemPromptOverlay(promptAugmenter, agentId, runtimeContext) {
|
|
|
2189
2190
|
/** Phase 9a: the def's `buildArgs` 4th argument — `undefined` when the run selects no model/reasoning/permissionMode/overlay/tool-restriction at all (byte-identical to omitting the argument). Pure. */
|
|
2190
2191
|
export function buildAgentBuildArgsOptions(input, systemPromptOverlay) {
|
|
2191
2192
|
const hasOverlay = systemPromptOverlay !== undefined && systemPromptOverlay !== null;
|
|
2192
|
-
|
|
2193
|
-
&& input.reasoning === undefined
|
|
2194
|
-
&& input.permissionMode === undefined
|
|
2195
|
-
&& input.disallowedTools === undefined
|
|
2196
|
-
&& input.allowedTools === undefined
|
|
2197
|
-
&& !hasOverlay) {
|
|
2198
|
-
return undefined;
|
|
2199
|
-
}
|
|
2200
|
-
return {
|
|
2193
|
+
const options = {
|
|
2201
2194
|
...(input.model !== undefined ? { model: input.model } : {}),
|
|
2202
2195
|
...(input.reasoning !== undefined ? { reasoning: input.reasoning } : {}),
|
|
2203
2196
|
...(input.permissionMode !== undefined ? { permissionMode: input.permissionMode } : {}),
|
|
2204
2197
|
...(input.disallowedTools !== undefined ? { disallowedTools: input.disallowedTools } : {}),
|
|
2205
2198
|
...(input.allowedTools !== undefined ? { allowedTools: input.allowedTools } : {}),
|
|
2199
|
+
...(input.settingSources !== undefined ? { settingSources: input.settingSources } : {}),
|
|
2200
|
+
...(input.settings !== undefined ? { settings: input.settings } : {}),
|
|
2206
2201
|
...(hasOverlay ? { systemPromptOverlay } : {}),
|
|
2207
2202
|
};
|
|
2203
|
+
return Object.keys(options).length === 0 ? undefined : options;
|
|
2208
2204
|
}
|
|
2209
2205
|
/**
|
|
2210
2206
|
* **The single dispatch point from a computed system-prompt overlay to its delivery mechanism** —
|
|
@@ -2581,7 +2577,7 @@ export async function runPiRpcDispatch(input, deps) {
|
|
|
2581
2577
|
}
|
|
2582
2578
|
export function createAgentExecutor(options) {
|
|
2583
2579
|
const lifecycle = options.lifecycle;
|
|
2584
|
-
const { getAgentDef: getAgentDefFn, resolveAgentLaunch: resolveAgentLaunchFn, applyAgentLaunchEnv: applyAgentLaunchEnvFn, attachAcpSession: attachAcpSessionFn, attachPiRpcSession: attachPiRpcSessionFn, preparePromptFileForAgent: preparePromptFileForAgentFn, prepareAgentLogFile: prepareAgentLogFileFn, } = resolveAgentRuntimeDeps(options);
|
|
2580
|
+
const { getAgentDef: getAgentDefFn, resolveAgentLaunch: resolveAgentLaunchFn, ensureAgentCapabilities: ensureAgentCapabilitiesFn, applyAgentLaunchEnv: applyAgentLaunchEnvFn, attachAcpSession: attachAcpSessionFn, attachPiRpcSession: attachPiRpcSessionFn, preparePromptFileForAgent: preparePromptFileForAgentFn, prepareAgentLogFile: prepareAgentLogFileFn, } = resolveAgentRuntimeDeps(options);
|
|
2585
2581
|
const { createCommandInvocation: createCommandInvocationFn, spawn: spawnFn, listProcessSnapshots: listProcessSnapshotsFn, collectProcessTreePids: collectProcessTreePidsFn, stopProcesses: stopProcessesFn, } = resolveProcessDeps(options);
|
|
2586
2582
|
const { onCleanupFailure: onCleanupFailureFn, bufferedStdoutMaxBytes } = resolveMiscExecutorDeps(options);
|
|
2587
2583
|
const journal = options.journal;
|
|
@@ -2604,6 +2600,13 @@ export function createAgentExecutor(options) {
|
|
|
2604
2600
|
* @overallScore 100/100
|
|
2605
2601
|
*/
|
|
2606
2602
|
async function failBeforeSpawn(runId, code, message) {
|
|
2603
|
+
// The reason goes on the run's own stream before `end`, not only into the thrown error: the
|
|
2604
|
+
// thrown error reaches the server log, while a chat client sees only this stream. Without it
|
|
2605
|
+
// the user got a bare "Run failed". Best-effort: a stream that will not take the event must not
|
|
2606
|
+
// stop the run from finishing.
|
|
2607
|
+
await lifecycle
|
|
2608
|
+
.emit(runId, { event: 'error', data: { message: `The assistant could not start: ${message.replace(/^AgentExecutor:\s*/, '')}` } })
|
|
2609
|
+
.catch(() => undefined);
|
|
2607
2610
|
await lifecycle.finish({ runId, status: 'failed', code: null, signal: null, resumable: false });
|
|
2608
2611
|
throw new AgentExecutorError(code, message);
|
|
2609
2612
|
}
|
|
@@ -2660,9 +2663,17 @@ export function createAgentExecutor(options) {
|
|
|
2660
2663
|
prompt: imageDelivery.prompt,
|
|
2661
2664
|
resumeSessionId: preStagingRuntimeContext?.resumeSessionId,
|
|
2662
2665
|
});
|
|
2663
|
-
|
|
2666
|
+
// The def's own declared env (e.g. claude's `ENABLE_TOOL_SEARCH=false`) is a default the run's
|
|
2667
|
+
// env can override. Detection already spawns with it (`detection.ts`); without this the run
|
|
2668
|
+
// spawn silently dropped it. Declared by the def itself, so it never widens SEC-001's allowlist.
|
|
2669
|
+
const resolvedEnv = { ...(def.env ?? {}), ...resolveRunEnv(input, process.env) };
|
|
2664
2670
|
const launch = await resolveLaunch({ runId: input.runId, def, resolvedEnv }, { resolveAgentLaunch: resolveAgentLaunchFn, failBeforeSpawn });
|
|
2665
2671
|
const spawnEnv = applyAgentLaunchEnvFn({ ...resolvedEnv }, launch);
|
|
2672
|
+
// Fill the def's `--help` capability gate before `buildArgs` reads it. Without this, only a
|
|
2673
|
+
// host that happened to call `detectAgents` in THIS process ever had the gate filled, so e.g.
|
|
2674
|
+
// `claude` never got `--include-partial-messages` (no streamed text). Probes once per binary;
|
|
2675
|
+
// never rejects (a failed probe keeps the safe no-optional-flags baseline).
|
|
2676
|
+
await ensureAgentCapabilitiesFn(def, launch.launchPath, spawnEnv);
|
|
2666
2677
|
// Stage a promptViaFile def's (grok-build) prompt to a temp file before buildArgs runs — its
|
|
2667
2678
|
// buildArgs throws without runtimeContext.promptFilePath. A no-op (returns null) for every
|
|
2668
2679
|
// def without promptViaFile: true (preparePromptFileForAgent's own guard).
|
package/dist/run-lifecycle.js
CHANGED
|
@@ -674,7 +674,13 @@ export function createRunLifecycle(input) {
|
|
|
674
674
|
throw new Error(`RunLifecycle: cannot emit "${driverInput.event}" on terminal run "${runId}" — drivers must stop emitting once finish() has been called`);
|
|
675
675
|
}
|
|
676
676
|
record.watchdog?.noteActivity();
|
|
677
|
-
|
|
677
|
+
// The crash watchdog above counts ANY output (the process is alive). The slow-run notice asks
|
|
678
|
+
// a different question — "has the person seen anything new?" — so only `agent` events count.
|
|
679
|
+
// A CLI's raw stdout/stderr (e.g. Claude Code's 30 s `tool_progress` heartbeats during a long
|
|
680
|
+
// tool call) is not rendered, and letting it reset this window meant the notice could never
|
|
681
|
+
// fire during exactly the long, silent tool calls it exists for.
|
|
682
|
+
if (driverInput.event === 'agent')
|
|
683
|
+
record.slowRunWatchdog?.noteActivity();
|
|
678
684
|
return appendEvent(runId, record, driverInput.event, driverInput.data);
|
|
679
685
|
},
|
|
680
686
|
async finish(finishInput) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/daemon",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "The stateful agent daemon runtime: run lifecycle, durable event-log port, the tool-execution boundary, agent executor, terminal sessions, and routines.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"registry": "https://registry.npmjs.org"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@jini-ai/agent-runtime": "0.3.
|
|
42
|
-
"@jini-ai/platform": "0.3.0",
|
|
41
|
+
"@jini-ai/agent-runtime": "0.3.10",
|
|
43
42
|
"@jini-ai/core": "0.3.1",
|
|
44
|
-
"@jini-ai/
|
|
43
|
+
"@jini-ai/platform": "0.3.0",
|
|
44
|
+
"@jini-ai/protocol": "0.3.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@vitest/coverage-v8": "^2.1.9",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module artifacts
|
|
3
|
-
*
|
|
4
|
-
* The generic artifact-store kernel port, ported from OD's
|
|
5
|
-
* `apps/daemon/src/artifacts/` (6 files). See `source-map.md` for full
|
|
6
|
-
* per-file provenance and the design decisions behind each generalization.
|
|
7
|
-
*/
|
|
8
|
-
export * from './manifest.js';
|
|
9
|
-
export * from './store.js';
|
|
10
|
-
export * from './publication-guard.js';
|
|
11
|
-
export * from './runtime-compat.js';
|
|
12
|
-
export * from './stub-guard.js';
|
|
13
|
-
export * from './text-suppression.js';
|
|
14
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/artifacts/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module artifacts
|
|
3
|
-
*
|
|
4
|
-
* The generic artifact-store kernel port, ported from OD's
|
|
5
|
-
* `apps/daemon/src/artifacts/` (6 files). See `source-map.md` for full
|
|
6
|
-
* per-file provenance and the design decisions behind each generalization.
|
|
7
|
-
*/
|
|
8
|
-
export * from './manifest.js';
|
|
9
|
-
export * from './store.js';
|
|
10
|
-
export * from './publication-guard.js';
|
|
11
|
-
export * from './runtime-compat.js';
|
|
12
|
-
export * from './stub-guard.js';
|
|
13
|
-
export * from './text-suppression.js';
|
|
14
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module artifacts/manifest
|
|
3
|
-
*
|
|
4
|
-
* Generic artifact-manifest validation: bounded-length string checks, path-
|
|
5
|
-
* traversal rejection on `entry`/`primary`/`supportingFiles`, a JSON-size
|
|
6
|
-
* cap on `metadata`, and version/timestamp stamping. Ported from OD's
|
|
7
|
-
* `apps/daemon/src/artifacts/manifest.ts`.
|
|
8
|
-
*
|
|
9
|
-
* De-branded: the origin's `kind`/`renderer` fields were validated against
|
|
10
|
-
* hardcoded `ALLOWED_KINDS`/`ALLOWED_RENDERERS` sets that bake in OD's own
|
|
11
|
-
* artifact taxonomy — including a literal `'design-system'` kind, OD's own
|
|
12
|
-
* product concept, not a generic engine noun. Both sets are now a
|
|
13
|
-
* caller-supplied {@link ArtifactManifestTaxonomy} instead of module
|
|
14
|
-
* constants — a host (like an OD adapter) supplies its own kind/renderer/
|
|
15
|
-
* export vocabulary; the validation *mechanism* (bounded strings, traversal
|
|
16
|
-
* rejection, size caps, version stamping) is what this module owns. `status`
|
|
17
|
-
* (`'streaming' | 'complete' | 'error'`) is kept as a fixed literal union —
|
|
18
|
-
* that's a generic artifact-lifecycle concept, not a product taxonomy.
|
|
19
|
-
*/
|
|
20
|
-
export type ArtifactStatus = 'streaming' | 'complete' | 'error';
|
|
21
|
-
/**
|
|
22
|
-
* A host's own artifact-kind/renderer/export vocabulary. The engine treats
|
|
23
|
-
* every value as an opaque string it only bounds-checks and validates
|
|
24
|
-
* membership against — it assigns no meaning to any particular kind name.
|
|
25
|
-
*/
|
|
26
|
-
export interface ArtifactManifestTaxonomy {
|
|
27
|
-
readonly allowedKinds: ReadonlySet<string>;
|
|
28
|
-
readonly allowedRenderers: ReadonlySet<string>;
|
|
29
|
-
readonly allowedExports: ReadonlySet<string>;
|
|
30
|
-
}
|
|
31
|
-
/** A taxonomy that accepts nothing — forces a host to supply its own before any manifest can validate. */
|
|
32
|
-
export declare const emptyArtifactManifestTaxonomy: ArtifactManifestTaxonomy;
|
|
33
|
-
export interface ArtifactManifest {
|
|
34
|
-
readonly version: number;
|
|
35
|
-
readonly kind: string;
|
|
36
|
-
readonly title: string;
|
|
37
|
-
readonly entry: string;
|
|
38
|
-
readonly renderer: string;
|
|
39
|
-
readonly status: ArtifactStatus;
|
|
40
|
-
readonly exports: readonly string[];
|
|
41
|
-
readonly primary?: string | true;
|
|
42
|
-
readonly supportingFiles?: readonly string[];
|
|
43
|
-
readonly createdAt: string;
|
|
44
|
-
readonly updatedAt: string;
|
|
45
|
-
/** Generic "what produced or relates to this artifact" reference (OD: a skill id). Opaque to the engine. */
|
|
46
|
-
readonly sourceContextId?: string;
|
|
47
|
-
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
48
|
-
}
|
|
49
|
-
type JsonRecord = Record<string, unknown>;
|
|
50
|
-
type ValidationResult = {
|
|
51
|
-
ok: true;
|
|
52
|
-
value: ArtifactManifest | null;
|
|
53
|
-
} | {
|
|
54
|
-
ok: false;
|
|
55
|
-
error: string;
|
|
56
|
-
};
|
|
57
|
-
export interface ValidateManifestOptions {
|
|
58
|
-
/** Keep an existing `updatedAt` instead of stamping the current time (used when re-parsing a persisted manifest). */
|
|
59
|
-
preserveUpdatedAt?: boolean;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Validates a freeform manifest payload against `taxonomy`, returning a
|
|
63
|
-
* sanitized, stamped {@link ArtifactManifest} on success. `manifest == null`
|
|
64
|
-
* is a valid "no manifest supplied" outcome (`{ ok: true, value: null }`) —
|
|
65
|
-
* callers that require a manifest check for `value === null` themselves
|
|
66
|
-
* (see `store.ts`'s `resolveArtifactManifest`).
|
|
67
|
-
*/
|
|
68
|
-
export declare function validateArtifactManifestInput(manifest: unknown, entry: unknown, taxonomy: ArtifactManifestTaxonomy, options?: ValidateManifestOptions): ValidationResult;
|
|
69
|
-
/** Normalizes a validated manifest payload into the stamped, canonical {@link ArtifactManifest} shape. */
|
|
70
|
-
export declare function sanitizeManifest(manifest: JsonRecord, entry: string, options?: ValidateManifestOptions): ArtifactManifest;
|
|
71
|
-
/**
|
|
72
|
-
* Re-validates a manifest read back from persistence (e.g. a sidecar file),
|
|
73
|
-
* preserving its original `updatedAt`. Returns `null` on any validation
|
|
74
|
-
* failure or version mismatch/parse error rather than throwing — a corrupt
|
|
75
|
-
* or stale sidecar should degrade to "no manifest", not crash the reader.
|
|
76
|
-
*/
|
|
77
|
-
export declare function parsePersistedManifest(raw: string, fallbackEntry: string, taxonomy: ArtifactManifestTaxonomy): ArtifactManifest | null;
|
|
78
|
-
/**
|
|
79
|
-
* Injectable seam for a host's own "what kind of artifact is this path"
|
|
80
|
-
* heuristic (OD: extension-based HTML/deck/markdown/svg inference). The
|
|
81
|
-
* engine has no default beyond "infer nothing" — see
|
|
82
|
-
* {@link noopManifestInferrer} — a host wires in its own file-kind
|
|
83
|
-
* classification (per the task brief: "keep OD's file-kind classification
|
|
84
|
-
* as adapter").
|
|
85
|
-
*/
|
|
86
|
-
export type ManifestInferrer = (entry: string) => Partial<ArtifactManifest> | null;
|
|
87
|
-
/** Infers nothing — every create call must supply an explicit manifest. */
|
|
88
|
-
export declare const noopManifestInferrer: ManifestInferrer;
|
|
89
|
-
export {};
|
|
90
|
-
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module artifacts/manifest
|
|
3
|
-
*
|
|
4
|
-
* Generic artifact-manifest validation: bounded-length string checks, path-
|
|
5
|
-
* traversal rejection on `entry`/`primary`/`supportingFiles`, a JSON-size
|
|
6
|
-
* cap on `metadata`, and version/timestamp stamping. Ported from OD's
|
|
7
|
-
* `apps/daemon/src/artifacts/manifest.ts`.
|
|
8
|
-
*
|
|
9
|
-
* De-branded: the origin's `kind`/`renderer` fields were validated against
|
|
10
|
-
* hardcoded `ALLOWED_KINDS`/`ALLOWED_RENDERERS` sets that bake in OD's own
|
|
11
|
-
* artifact taxonomy — including a literal `'design-system'` kind, OD's own
|
|
12
|
-
* product concept, not a generic engine noun. Both sets are now a
|
|
13
|
-
* caller-supplied {@link ArtifactManifestTaxonomy} instead of module
|
|
14
|
-
* constants — a host (like an OD adapter) supplies its own kind/renderer/
|
|
15
|
-
* export vocabulary; the validation *mechanism* (bounded strings, traversal
|
|
16
|
-
* rejection, size caps, version stamping) is what this module owns. `status`
|
|
17
|
-
* (`'streaming' | 'complete' | 'error'`) is kept as a fixed literal union —
|
|
18
|
-
* that's a generic artifact-lifecycle concept, not a product taxonomy.
|
|
19
|
-
*/
|
|
20
|
-
const MANIFEST_VERSION = 1;
|
|
21
|
-
const MAX_TITLE_LENGTH = 200;
|
|
22
|
-
const MAX_SOURCE_CONTEXT_ID_LENGTH = 128;
|
|
23
|
-
const MAX_SUPPORTING_FILE_LENGTH = 260;
|
|
24
|
-
const MAX_SUPPORTING_FILES = 128;
|
|
25
|
-
const MAX_METADATA_BYTES = 16 * 1024;
|
|
26
|
-
const ALLOWED_STATUS = new Set(['streaming', 'complete', 'error']);
|
|
27
|
-
/** A taxonomy that accepts nothing — forces a host to supply its own before any manifest can validate. */
|
|
28
|
-
export const emptyArtifactManifestTaxonomy = {
|
|
29
|
-
allowedKinds: new Set(),
|
|
30
|
-
allowedRenderers: new Set(),
|
|
31
|
-
allowedExports: new Set(),
|
|
32
|
-
};
|
|
33
|
-
function isPlainObject(value) {
|
|
34
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
35
|
-
return false;
|
|
36
|
-
const proto = Object.getPrototypeOf(value);
|
|
37
|
-
return proto === Object.prototype || proto === null;
|
|
38
|
-
}
|
|
39
|
-
function validateBoundedString(value, field, maxLen, { allowEmpty = false } = {}) {
|
|
40
|
-
if (typeof value !== 'string')
|
|
41
|
-
return `${field} must be a string`;
|
|
42
|
-
if (!allowEmpty && value.length === 0)
|
|
43
|
-
return `${field} is required`;
|
|
44
|
-
if (value.length > maxLen)
|
|
45
|
-
return `${field} exceeds max length (${maxLen})`;
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
function validateSupportingPath(value) {
|
|
49
|
-
if (typeof value !== 'string')
|
|
50
|
-
return 'supportingFiles entries must be strings';
|
|
51
|
-
if (value.length === 0)
|
|
52
|
-
return 'supportingFiles entries cannot be empty';
|
|
53
|
-
if (value.length > MAX_SUPPORTING_FILE_LENGTH) {
|
|
54
|
-
return `supportingFiles entries exceed max length (${MAX_SUPPORTING_FILE_LENGTH})`;
|
|
55
|
-
}
|
|
56
|
-
if (/^[A-Za-z]:/.test(value) || value.startsWith('/')) {
|
|
57
|
-
return 'supportingFiles cannot contain absolute paths';
|
|
58
|
-
}
|
|
59
|
-
if (value.includes('\u0000'))
|
|
60
|
-
return 'supportingFiles cannot contain null bytes';
|
|
61
|
-
const normalized = value.replace(/\\/g, '/');
|
|
62
|
-
if (normalized.includes('..'))
|
|
63
|
-
return 'supportingFiles cannot contain traversal segments';
|
|
64
|
-
const parts = normalized.split('/').filter(Boolean);
|
|
65
|
-
if (parts.length === 0 || parts.some((p) => p === '.' || p === '..')) {
|
|
66
|
-
return 'supportingFiles cannot contain traversal segments';
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Validates a freeform manifest payload against `taxonomy`, returning a
|
|
72
|
-
* sanitized, stamped {@link ArtifactManifest} on success. `manifest == null`
|
|
73
|
-
* is a valid "no manifest supplied" outcome (`{ ok: true, value: null }`) —
|
|
74
|
-
* callers that require a manifest check for `value === null` themselves
|
|
75
|
-
* (see `store.ts`'s `resolveArtifactManifest`).
|
|
76
|
-
*/
|
|
77
|
-
export function validateArtifactManifestInput(manifest, entry, taxonomy, options = {}) {
|
|
78
|
-
if (manifest == null)
|
|
79
|
-
return { ok: true, value: null };
|
|
80
|
-
if (!isPlainObject(manifest)) {
|
|
81
|
-
return { ok: false, error: 'artifactManifest must be an object' };
|
|
82
|
-
}
|
|
83
|
-
const kindErr = validateBoundedString(manifest.kind, 'artifactManifest.kind', 64);
|
|
84
|
-
if (kindErr)
|
|
85
|
-
return { ok: false, error: kindErr };
|
|
86
|
-
// Cast, not a runtime guard: validateBoundedString's first check is
|
|
87
|
-
// `typeof value !== 'string'`, which already returned above for any
|
|
88
|
-
// non-string `kind` — this only narrows the type for `taxonomy.allowedKinds.has`.
|
|
89
|
-
if (!taxonomy.allowedKinds.has(manifest.kind)) {
|
|
90
|
-
return { ok: false, error: 'artifactManifest.kind is not allowed' };
|
|
91
|
-
}
|
|
92
|
-
const rendererErr = validateBoundedString(manifest.renderer, 'artifactManifest.renderer', 64);
|
|
93
|
-
if (rendererErr)
|
|
94
|
-
return { ok: false, error: rendererErr };
|
|
95
|
-
// Same reasoning as `kind` above.
|
|
96
|
-
if (!taxonomy.allowedRenderers.has(manifest.renderer)) {
|
|
97
|
-
return { ok: false, error: 'artifactManifest.renderer is not allowed' };
|
|
98
|
-
}
|
|
99
|
-
if (!Array.isArray(manifest.exports) || manifest.exports.length === 0) {
|
|
100
|
-
return { ok: false, error: 'artifactManifest.exports must be a non-empty array' };
|
|
101
|
-
}
|
|
102
|
-
for (const exp of manifest.exports) {
|
|
103
|
-
if (typeof exp !== 'string') {
|
|
104
|
-
return { ok: false, error: 'artifactManifest.exports must contain strings' };
|
|
105
|
-
}
|
|
106
|
-
if (!taxonomy.allowedExports.has(exp)) {
|
|
107
|
-
return { ok: false, error: `artifactManifest.exports contains unsupported value: ${exp}` };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
if (manifest.status !== undefined) {
|
|
111
|
-
if (typeof manifest.status !== 'string') {
|
|
112
|
-
return { ok: false, error: 'artifactManifest.status must be a string' };
|
|
113
|
-
}
|
|
114
|
-
if (!ALLOWED_STATUS.has(manifest.status)) {
|
|
115
|
-
return { ok: false, error: 'artifactManifest.status is not allowed' };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (manifest.primary !== undefined) {
|
|
119
|
-
if (manifest.primary !== true) {
|
|
120
|
-
const primaryErr = validateSupportingPath(manifest.primary);
|
|
121
|
-
if (primaryErr)
|
|
122
|
-
return { ok: false, error: `artifactManifest.primary ${primaryErr}` };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (manifest.supportingFiles !== undefined) {
|
|
126
|
-
if (!Array.isArray(manifest.supportingFiles)) {
|
|
127
|
-
return { ok: false, error: 'artifactManifest.supportingFiles must be an array' };
|
|
128
|
-
}
|
|
129
|
-
if (manifest.supportingFiles.length > MAX_SUPPORTING_FILES) {
|
|
130
|
-
return {
|
|
131
|
-
ok: false,
|
|
132
|
-
error: `artifactManifest.supportingFiles exceeds max items (${MAX_SUPPORTING_FILES})`,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
for (const rel of manifest.supportingFiles) {
|
|
136
|
-
const relErr = validateSupportingPath(rel);
|
|
137
|
-
if (relErr)
|
|
138
|
-
return { ok: false, error: relErr };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (manifest.title !== undefined) {
|
|
142
|
-
const titleErr = validateBoundedString(manifest.title, 'artifactManifest.title', MAX_TITLE_LENGTH, {
|
|
143
|
-
allowEmpty: false,
|
|
144
|
-
});
|
|
145
|
-
if (titleErr)
|
|
146
|
-
return { ok: false, error: titleErr };
|
|
147
|
-
}
|
|
148
|
-
if (manifest.sourceContextId !== undefined) {
|
|
149
|
-
const sourceErr = validateBoundedString(manifest.sourceContextId, 'artifactManifest.sourceContextId', MAX_SOURCE_CONTEXT_ID_LENGTH, { allowEmpty: true });
|
|
150
|
-
if (sourceErr)
|
|
151
|
-
return { ok: false, error: sourceErr };
|
|
152
|
-
}
|
|
153
|
-
if (manifest.metadata !== undefined) {
|
|
154
|
-
if (!isPlainObject(manifest.metadata)) {
|
|
155
|
-
return { ok: false, error: 'artifactManifest.metadata must be a plain object' };
|
|
156
|
-
}
|
|
157
|
-
// JSON.stringify always returns a string (never undefined) when called
|
|
158
|
-
// directly on a plain object — undefined-return only happens when the
|
|
159
|
-
// top-level argument itself is a function/symbol/undefined, which
|
|
160
|
-
// isPlainObject above already rejected.
|
|
161
|
-
const serialized = JSON.stringify(manifest.metadata);
|
|
162
|
-
if (Buffer.byteLength(serialized, 'utf8') > MAX_METADATA_BYTES) {
|
|
163
|
-
return {
|
|
164
|
-
ok: false,
|
|
165
|
-
error: `artifactManifest.metadata exceeds max size (${MAX_METADATA_BYTES} bytes)`,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const manifestEntry = typeof manifest.entry === 'string' && manifest.entry.trim() ? manifest.entry.trim() : entry;
|
|
170
|
-
const entryErr = validateSupportingPath(manifestEntry);
|
|
171
|
-
if (entryErr) {
|
|
172
|
-
return { ok: false, error: `artifactManifest.entry ${entryErr}` };
|
|
173
|
-
}
|
|
174
|
-
const safeEntry = manifestEntry.replace(/\\/g, '/');
|
|
175
|
-
return { ok: true, value: sanitizeManifest(manifest, safeEntry, options) };
|
|
176
|
-
}
|
|
177
|
-
/** Normalizes a validated manifest payload into the stamped, canonical {@link ArtifactManifest} shape. */
|
|
178
|
-
export function sanitizeManifest(manifest, entry, options = {}) {
|
|
179
|
-
const now = new Date().toISOString();
|
|
180
|
-
const supportingFiles = Array.isArray(manifest.supportingFiles)
|
|
181
|
-
? manifest.supportingFiles.map((x) => String(x).replace(/\\/g, '/'))
|
|
182
|
-
: undefined;
|
|
183
|
-
const primary = manifest.primary === true
|
|
184
|
-
? true
|
|
185
|
-
: typeof manifest.primary === 'string'
|
|
186
|
-
? manifest.primary.replace(/\\/g, '/')
|
|
187
|
-
: undefined;
|
|
188
|
-
return {
|
|
189
|
-
version: MANIFEST_VERSION,
|
|
190
|
-
kind: manifest.kind,
|
|
191
|
-
title: manifest.title || entry,
|
|
192
|
-
entry,
|
|
193
|
-
renderer: manifest.renderer,
|
|
194
|
-
status: typeof manifest.status === 'string' && ALLOWED_STATUS.has(manifest.status)
|
|
195
|
-
? manifest.status
|
|
196
|
-
: 'complete',
|
|
197
|
-
exports: manifest.exports,
|
|
198
|
-
...(primary !== undefined ? { primary } : {}),
|
|
199
|
-
...(supportingFiles !== undefined ? { supportingFiles } : {}),
|
|
200
|
-
createdAt: typeof manifest.createdAt === 'string' ? manifest.createdAt : now,
|
|
201
|
-
updatedAt: options.preserveUpdatedAt && typeof manifest.updatedAt === 'string'
|
|
202
|
-
? manifest.updatedAt
|
|
203
|
-
: now,
|
|
204
|
-
...(manifest.sourceContextId !== undefined ? { sourceContextId: manifest.sourceContextId } : {}),
|
|
205
|
-
...(manifest.metadata !== undefined ? { metadata: manifest.metadata } : {}),
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Re-validates a manifest read back from persistence (e.g. a sidecar file),
|
|
210
|
-
* preserving its original `updatedAt`. Returns `null` on any validation
|
|
211
|
-
* failure or version mismatch/parse error rather than throwing — a corrupt
|
|
212
|
-
* or stale sidecar should degrade to "no manifest", not crash the reader.
|
|
213
|
-
*/
|
|
214
|
-
export function parsePersistedManifest(raw, fallbackEntry, taxonomy) {
|
|
215
|
-
try {
|
|
216
|
-
const parsed = JSON.parse(raw);
|
|
217
|
-
if (!isPlainObject(parsed) || parsed.version !== MANIFEST_VERSION)
|
|
218
|
-
return null;
|
|
219
|
-
const entry = typeof parsed.entry === 'string' && parsed.entry ? parsed.entry : fallbackEntry;
|
|
220
|
-
const result = validateArtifactManifestInput(parsed, entry, taxonomy, { preserveUpdatedAt: true });
|
|
221
|
-
return result.ok ? result.value : null;
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/** Infers nothing — every create call must supply an explicit manifest. */
|
|
228
|
-
export const noopManifestInferrer = () => null;
|
|
229
|
-
//# sourceMappingURL=manifest.js.map
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export declare const ARTIFACT_PUBLICATION_BLOCKED_CODE: "ARTIFACT_PUBLICATION_BLOCKED";
|
|
2
|
-
export interface PublicationGuardConfig {
|
|
3
|
-
/** Artifact kinds this guard scans. A kind not in this set is never blocked, whatever its content. */
|
|
4
|
-
readonly guardedKinds: ReadonlySet<string>;
|
|
5
|
-
/** Substrings that mark an artifact as containing an unresolved placeholder. Matching is case-sensitive. */
|
|
6
|
-
readonly blockedPlaceholders: readonly string[];
|
|
7
|
-
}
|
|
8
|
-
/** A guard that blocks nothing — no guarded kinds, no placeholder markers configured. */
|
|
9
|
-
export declare const emptyPublicationGuardConfig: PublicationGuardConfig;
|
|
10
|
-
export declare class ArtifactPublicationBlockedError extends Error {
|
|
11
|
-
readonly code: "ARTIFACT_PUBLICATION_BLOCKED";
|
|
12
|
-
readonly placeholders: readonly string[];
|
|
13
|
-
constructor(placeholders: readonly string[]);
|
|
14
|
-
}
|
|
15
|
-
export declare function isPublicationGuardedKind(kind: unknown, config: PublicationGuardConfig): boolean;
|
|
16
|
-
/** Returns every configured placeholder marker found in `value`'s stringified content, in declaration order. */
|
|
17
|
-
export declare function findBlockedPlaceholders(value: unknown, config: PublicationGuardConfig): string[];
|
|
18
|
-
export declare function shouldBlockPublication(value: unknown, config: PublicationGuardConfig): boolean;
|
|
19
|
-
export declare function buildArtifactPublicationBlockedMessage(placeholders: readonly string[]): string;
|
|
20
|
-
/**
|
|
21
|
-
* Throws {@link ArtifactPublicationBlockedError} when `value` (for a
|
|
22
|
-
* `kind` in `config.guardedKinds`) contains any of `config.blockedPlaceholders`.
|
|
23
|
-
* No-ops for a kind outside `config.guardedKinds` — a guard scoped to
|
|
24
|
-
* user-facing rendered documents shouldn't reject content in other kinds
|
|
25
|
-
* where the same substrings could be legitimate.
|
|
26
|
-
*/
|
|
27
|
-
export declare function assertArtifactPublicationAllowed(kind: unknown, value: unknown, config: PublicationGuardConfig): void;
|
|
28
|
-
//# sourceMappingURL=publication-guard.d.ts.map
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module artifacts/publication-guard
|
|
3
|
-
*
|
|
4
|
-
* A pre-publish content guard: blocks an artifact write when its body
|
|
5
|
-
* contains a caller-declared "unresolved placeholder" marker, scoped to a
|
|
6
|
-
* caller-declared set of guarded kinds. Ported from OD's
|
|
7
|
-
* `apps/daemon/src/artifacts/publication-guard.ts`.
|
|
8
|
-
*
|
|
9
|
-
* De-branded: the origin hardcoded `UNRESOLVED_ARTIFACT_PLACEHOLDERS` — five
|
|
10
|
-
* literal strings ("Name to confirm", "$X.XM", ...) lifted from one specific
|
|
11
|
-
* bundled example template's pitch-deck fill-in-the-blank convention — and
|
|
12
|
-
* `PUBLICATION_GUARDED_ARTIFACT_KINDS = {'html', 'deck'}`. Both are entirely
|
|
13
|
-
* OD's own content, not a generic engine mechanism, so this module now takes
|
|
14
|
-
* a caller-supplied {@link PublicationGuardConfig} (empty by default — a
|
|
15
|
-
* guard that never blocks anything until a host configures it) instead of
|
|
16
|
-
* baking in that one template's marker strings. The guard *mechanism*
|
|
17
|
-
* (scan a guarded-kind body for any configured marker, throw before
|
|
18
|
-
* publish) is what this module owns.
|
|
19
|
-
*/
|
|
20
|
-
import { Buffer } from 'node:buffer';
|
|
21
|
-
export const ARTIFACT_PUBLICATION_BLOCKED_CODE = 'ARTIFACT_PUBLICATION_BLOCKED';
|
|
22
|
-
/** A guard that blocks nothing — no guarded kinds, no placeholder markers configured. */
|
|
23
|
-
export const emptyPublicationGuardConfig = {
|
|
24
|
-
guardedKinds: new Set(),
|
|
25
|
-
blockedPlaceholders: [],
|
|
26
|
-
};
|
|
27
|
-
export class ArtifactPublicationBlockedError extends Error {
|
|
28
|
-
code = ARTIFACT_PUBLICATION_BLOCKED_CODE;
|
|
29
|
-
placeholders;
|
|
30
|
-
constructor(placeholders) {
|
|
31
|
-
super(buildArtifactPublicationBlockedMessage(placeholders));
|
|
32
|
-
this.name = 'ArtifactPublicationBlockedError';
|
|
33
|
-
this.placeholders = [...placeholders];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
export function isPublicationGuardedKind(kind, config) {
|
|
37
|
-
return typeof kind === 'string' && config.guardedKinds.has(kind);
|
|
38
|
-
}
|
|
39
|
-
function stringifyArtifactContent(value) {
|
|
40
|
-
if (typeof value === 'string')
|
|
41
|
-
return value;
|
|
42
|
-
if (Buffer.isBuffer(value))
|
|
43
|
-
return value.toString('utf8');
|
|
44
|
-
if (value instanceof Uint8Array)
|
|
45
|
-
return Buffer.from(value).toString('utf8');
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
/** Returns every configured placeholder marker found in `value`'s stringified content, in declaration order. */
|
|
49
|
-
export function findBlockedPlaceholders(value, config) {
|
|
50
|
-
const text = stringifyArtifactContent(value);
|
|
51
|
-
if (!text)
|
|
52
|
-
return [];
|
|
53
|
-
return config.blockedPlaceholders.filter((placeholder) => text.includes(placeholder));
|
|
54
|
-
}
|
|
55
|
-
export function shouldBlockPublication(value, config) {
|
|
56
|
-
return findBlockedPlaceholders(value, config).length > 0;
|
|
57
|
-
}
|
|
58
|
-
export function buildArtifactPublicationBlockedMessage(placeholders) {
|
|
59
|
-
const list = placeholders.length > 0 ? placeholders.join(', ') : 'unknown placeholders';
|
|
60
|
-
return `Artifact still contains unresolved placeholders: ${list}. Resolve them before publishing.`;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Throws {@link ArtifactPublicationBlockedError} when `value` (for a
|
|
64
|
-
* `kind` in `config.guardedKinds`) contains any of `config.blockedPlaceholders`.
|
|
65
|
-
* No-ops for a kind outside `config.guardedKinds` — a guard scoped to
|
|
66
|
-
* user-facing rendered documents shouldn't reject content in other kinds
|
|
67
|
-
* where the same substrings could be legitimate.
|
|
68
|
-
*/
|
|
69
|
-
export function assertArtifactPublicationAllowed(kind, value, config) {
|
|
70
|
-
if (!isPublicationGuardedKind(kind, config))
|
|
71
|
-
return;
|
|
72
|
-
const placeholders = findBlockedPlaceholders(value, config);
|
|
73
|
-
if (placeholders.length > 0) {
|
|
74
|
-
throw new ArtifactPublicationBlockedError(placeholders);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
//# sourceMappingURL=publication-guard.js.map
|