arkgate 2.10.0 → 2.12.0
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/CHANGELOG.md +107 -0
- package/README.md +21 -12
- package/SECURITY.md +3 -4
- package/bin/ark-check.mjs +41 -16
- package/bin/ark-mcp.mjs +54 -10
- package/bin/ark.mjs +87 -24
- package/bin/lib/agent-gates.mjs +68 -2090
- package/bin/lib/architecture-scan.mjs +4 -1
- package/bin/lib/baseline-key.mjs +17 -0
- package/bin/lib/ci-and-commands.mjs +386 -0
- package/bin/lib/config-warnings.mjs +22 -0
- package/bin/lib/core-layers.mjs +7 -0
- package/bin/lib/core-ratchet.mjs +3 -7
- package/bin/lib/deploy-path.mjs +205 -0
- package/bin/lib/doctor-plan.mjs +29 -5
- package/bin/lib/gate-files.mjs +223 -0
- package/bin/lib/hook-templates.mjs +99 -0
- package/bin/lib/install-migrate.mjs +442 -0
- package/bin/lib/mcp-adoption.mjs +423 -0
- package/bin/lib/presets.mjs +3 -0
- package/bin/lib/safety-diagnostics.mjs +263 -0
- package/bin/lib/scan-files.mjs +51 -6
- package/bin/lib/skill-install.mjs +259 -0
- package/bin/lib/typescript-host.mjs +88 -0
- package/bin/lib/violations.mjs +3 -3
- package/bin/lib/write-path-detect.mjs +138 -0
- package/dist/index.cjs +103 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +103 -8
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +18 -5
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +1 -1
- package/dist/nestjs/index.d.ts +1 -1
- package/dist/nestjs/index.js +18 -5
- package/dist/nestjs/index.js.map +1 -1
- package/dist/runtime/index.cjs +103 -8
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +103 -8
- package/dist/runtime/index.js.map +1 -1
- package/dist/{types-D6Q8WHes.d.cts → types-BZ17b9i5.d.cts} +5 -1
- package/dist/{types-D6Q8WHes.d.ts → types-BZ17b9i5.d.ts} +5 -1
- package/docs/agent-guide.md +12 -2
- package/docs/ai-gates.md +20 -2
- package/docs/package-surface.md +10 -3
- package/docs/production-hardening.md +5 -0
- package/package.json +5 -2
- package/server.json +2 -2
- package/templates/skills/ark-autopilot.md +77 -45
- package/templates/skills/ark-explain.md +2 -1
- package/templates/skills/ark-explore.md +135 -34
package/dist/index.cjs
CHANGED
|
@@ -80,7 +80,7 @@ __export(index_exports, {
|
|
|
80
80
|
module.exports = __toCommonJS(index_exports);
|
|
81
81
|
|
|
82
82
|
// src/version.ts
|
|
83
|
-
var version = "2.
|
|
83
|
+
var version = "2.12.0";
|
|
84
84
|
|
|
85
85
|
// src/kernel/intent/IntentRegistry.ts
|
|
86
86
|
var IntentRegistry = class {
|
|
@@ -2262,6 +2262,75 @@ function extractModuleSpecifiers(source) {
|
|
|
2262
2262
|
}
|
|
2263
2263
|
return matches.sort((a, b) => a.index - b.index);
|
|
2264
2264
|
}
|
|
2265
|
+
function extractModuleSpecifiersAst(ts, source) {
|
|
2266
|
+
const sourceFile = ts.createSourceFile("generated.ts", source, ts.ScriptTarget.Latest, true);
|
|
2267
|
+
const matches = [];
|
|
2268
|
+
const push = (node, value, kind, typeOnly = false) => {
|
|
2269
|
+
matches.push({
|
|
2270
|
+
value,
|
|
2271
|
+
index: node.getStart(sourceFile),
|
|
2272
|
+
kind,
|
|
2273
|
+
typeOnly
|
|
2274
|
+
});
|
|
2275
|
+
};
|
|
2276
|
+
const visit = (node) => {
|
|
2277
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
|
|
2278
|
+
const clause = node.importClause;
|
|
2279
|
+
const namedBindings = clause?.namedBindings;
|
|
2280
|
+
const specifiersOnly = clause && !clause.name && namedBindings && ts.isNamedImports(namedBindings) && namedBindings.elements.length > 0 && namedBindings.elements.every((element) => element.isTypeOnly === true);
|
|
2281
|
+
push(
|
|
2282
|
+
node.moduleSpecifier,
|
|
2283
|
+
node.moduleSpecifier.text,
|
|
2284
|
+
"import",
|
|
2285
|
+
Boolean(clause?.isTypeOnly || specifiersOnly)
|
|
2286
|
+
);
|
|
2287
|
+
} else if (ts.isExportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
|
|
2288
|
+
const clause = node.exportClause;
|
|
2289
|
+
const specifiersOnly = clause && ts.isNamedExports(clause) && clause.elements.length > 0 && clause.elements.every((element) => element.isTypeOnly === true);
|
|
2290
|
+
push(
|
|
2291
|
+
node.moduleSpecifier,
|
|
2292
|
+
node.moduleSpecifier.text,
|
|
2293
|
+
"export",
|
|
2294
|
+
Boolean(node.isTypeOnly || specifiersOnly)
|
|
2295
|
+
);
|
|
2296
|
+
} else if (ts.isCallExpression(node) && node.arguments.length === 1) {
|
|
2297
|
+
const argument = node.arguments[0];
|
|
2298
|
+
const value = tsStringLiteralText(ts, argument);
|
|
2299
|
+
if (value !== void 0 && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
2300
|
+
push(argument, value, "dynamic-import");
|
|
2301
|
+
} else if (value !== void 0 && ts.isIdentifier(node.expression) && node.expression.text === "require") {
|
|
2302
|
+
push(argument, value, "require");
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
ts.forEachChild(node, visit);
|
|
2306
|
+
};
|
|
2307
|
+
visit(sourceFile);
|
|
2308
|
+
return matches.sort((a, b) => a.index - b.index);
|
|
2309
|
+
}
|
|
2310
|
+
function nonLiteralDynamicImportLines(ts, source) {
|
|
2311
|
+
const sourceFile = ts.createSourceFile("generated.ts", source, ts.ScriptTarget.Latest, true);
|
|
2312
|
+
const lines = [];
|
|
2313
|
+
const visit = (node) => {
|
|
2314
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword && (!node.arguments[0] || !ts.isStringLiteralLike(node.arguments[0]))) {
|
|
2315
|
+
lines.push(sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1);
|
|
2316
|
+
}
|
|
2317
|
+
ts.forEachChild(node, visit);
|
|
2318
|
+
};
|
|
2319
|
+
visit(sourceFile);
|
|
2320
|
+
return lines;
|
|
2321
|
+
}
|
|
2322
|
+
function extractQuotedStringsAst(ts, source) {
|
|
2323
|
+
const sourceFile = ts.createSourceFile("generated.ts", source, ts.ScriptTarget.Latest, true);
|
|
2324
|
+
const matches = [];
|
|
2325
|
+
const visit = (node) => {
|
|
2326
|
+
if (ts.isStringLiteralLike(node)) {
|
|
2327
|
+
matches.push({ value: node.text, index: node.getStart(sourceFile) });
|
|
2328
|
+
}
|
|
2329
|
+
ts.forEachChild(node, visit);
|
|
2330
|
+
};
|
|
2331
|
+
visit(sourceFile);
|
|
2332
|
+
return matches;
|
|
2333
|
+
}
|
|
2265
2334
|
function looksLikeIntentName(s) {
|
|
2266
2335
|
return /^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(s);
|
|
2267
2336
|
}
|
|
@@ -2456,6 +2525,19 @@ function createAICodeGate(options = {}) {
|
|
|
2456
2525
|
const gateContext = context;
|
|
2457
2526
|
const filePath = gateContext?.filePath;
|
|
2458
2527
|
const contextLayer = gateContext?.layer;
|
|
2528
|
+
const moduleSpecifiers = options.typescript ? extractModuleSpecifiersAst(options.typescript, source) : extractModuleSpecifiers(source);
|
|
2529
|
+
const quotedStrings = options.typescript ? extractQuotedStringsAst(options.typescript, source) : extractQuotedStrings(source);
|
|
2530
|
+
if (options.typescript && !options.allowNonLiteralDynamicImport?.(filePath)) {
|
|
2531
|
+
for (const line of nonLiteralDynamicImportLines(options.typescript, source)) {
|
|
2532
|
+
violations.push(
|
|
2533
|
+
violation(
|
|
2534
|
+
"DYNAMIC_IMPORT_NOT_ALLOWLISTED",
|
|
2535
|
+
"Non-literal dynamic import cannot be resolved statically; add the reviewed file to dynamicImportAllowlist.",
|
|
2536
|
+
{ line, filePath }
|
|
2537
|
+
)
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2459
2541
|
const exemptFromInfraHeuristics = contextLayer !== void 0 && (explicitInfraLayers.has(contextLayer) || layerHasInfrastructureRole(contextLayer));
|
|
2460
2542
|
const infraLayerEscapeHatch = contextLayer !== void 0 ? ` If "${contextLayer}" is an infrastructure layer, mark it in ark.config.json with "mayImportInfrastructure": true (or name it with an infra token like Adapters/Persistence/Repository).` : "";
|
|
2461
2543
|
for (const pat of userForbidden) {
|
|
@@ -2481,7 +2563,7 @@ function createAICodeGate(options = {}) {
|
|
|
2481
2563
|
);
|
|
2482
2564
|
}
|
|
2483
2565
|
}
|
|
2484
|
-
for (const specifier of
|
|
2566
|
+
for (const specifier of moduleSpecifiers) {
|
|
2485
2567
|
const targetHit = options.resolveImportTarget?.(specifier.value, filePath) ?? (options.resolveImportLayer ? { layer: options.resolveImportLayer(specifier.value, filePath) } : void 0);
|
|
2486
2568
|
const sourceHit = typeof filePath === "string" ? options.resolveImportTarget?.(filePath) ?? (options.resolveImportLayer ? { layer: contextLayer, relPath: void 0 } : void 0) : void 0;
|
|
2487
2569
|
const targetLayer = targetHit?.layer;
|
|
@@ -2572,7 +2654,7 @@ function createAICodeGate(options = {}) {
|
|
|
2572
2654
|
}
|
|
2573
2655
|
}
|
|
2574
2656
|
if (enforceAllowlist && intentNames.size > 0) {
|
|
2575
|
-
for (const literal of
|
|
2657
|
+
for (const literal of quotedStrings) {
|
|
2576
2658
|
if (looksLikeIntentName(literal.value) && !intentNames.has(literal.value)) {
|
|
2577
2659
|
violations.push(
|
|
2578
2660
|
violation(
|
|
@@ -2590,7 +2672,7 @@ function createAICodeGate(options = {}) {
|
|
|
2590
2672
|
}
|
|
2591
2673
|
}
|
|
2592
2674
|
if (options.architectureProfile && contextLayer) {
|
|
2593
|
-
for (const literal of
|
|
2675
|
+
for (const literal of quotedStrings) {
|
|
2594
2676
|
if (!looksLikeIntentName(literal.value)) continue;
|
|
2595
2677
|
const targetLayer = options.architectureProfile.resolveLayer(literal.value);
|
|
2596
2678
|
if (!targetLayer) continue;
|
|
@@ -2866,15 +2948,18 @@ function sleep(ms) {
|
|
|
2866
2948
|
});
|
|
2867
2949
|
}
|
|
2868
2950
|
async function withTimeout(operation, timeoutMs, stepName) {
|
|
2869
|
-
|
|
2951
|
+
const controller = new AbortController();
|
|
2952
|
+
if (timeoutMs === void 0) return operation(controller.signal);
|
|
2870
2953
|
let timeout;
|
|
2871
2954
|
const timeoutPromise = new Promise((_, reject) => {
|
|
2872
2955
|
timeout = setTimeout(() => {
|
|
2873
|
-
|
|
2956
|
+
const error = new Error(`Workflow step "${stepName}" timed out after ${timeoutMs}ms.`);
|
|
2957
|
+
controller.abort(error);
|
|
2958
|
+
reject(error);
|
|
2874
2959
|
}, timeoutMs);
|
|
2875
2960
|
});
|
|
2876
2961
|
try {
|
|
2877
|
-
return await Promise.race([operation, timeoutPromise]);
|
|
2962
|
+
return await Promise.race([operation(controller.signal), timeoutPromise]);
|
|
2878
2963
|
} finally {
|
|
2879
2964
|
if (timeout) clearTimeout(timeout);
|
|
2880
2965
|
}
|
|
@@ -2909,6 +2994,15 @@ var WorkflowEngineImpl = class {
|
|
|
2909
2994
|
if (this.definitions.has(definition.name)) {
|
|
2910
2995
|
throw new Error(`Workflow "${definition.name}" is already registered.`);
|
|
2911
2996
|
}
|
|
2997
|
+
const names = /* @__PURE__ */ new Set();
|
|
2998
|
+
for (const step of definition.steps) {
|
|
2999
|
+
if (names.has(step.name)) {
|
|
3000
|
+
throw new Error(
|
|
3001
|
+
`Workflow "${definition.name}" has duplicate step name "${step.name}".`
|
|
3002
|
+
);
|
|
3003
|
+
}
|
|
3004
|
+
names.add(step.name);
|
|
3005
|
+
}
|
|
2912
3006
|
this.definitions.set(definition.name, definition);
|
|
2913
3007
|
if (definition.startOn) {
|
|
2914
3008
|
const trigger = definition.startOn;
|
|
@@ -2949,6 +3043,7 @@ var WorkflowEngineImpl = class {
|
|
|
2949
3043
|
} catch (err) {
|
|
2950
3044
|
await this.compensate(snapshot, definition.steps, err);
|
|
2951
3045
|
snapshot.status = "failed";
|
|
3046
|
+
snapshot.currentStep = void 0;
|
|
2952
3047
|
snapshot.error = errorMessage(err);
|
|
2953
3048
|
snapshot.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2954
3049
|
await this.store.save(snapshot);
|
|
@@ -2977,7 +3072,7 @@ var WorkflowEngineImpl = class {
|
|
|
2977
3072
|
await this.store.save(snapshot);
|
|
2978
3073
|
try {
|
|
2979
3074
|
const result = await withTimeout(
|
|
2980
|
-
Promise.resolve(step.execute(snapshot.context, this.bus)),
|
|
3075
|
+
(signal) => Promise.resolve(step.execute(snapshot.context, this.bus, signal)),
|
|
2981
3076
|
step.timeoutMs,
|
|
2982
3077
|
step.name
|
|
2983
3078
|
);
|