@akanjs/devkit 3.1.1 → 3.1.2-rc.1

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.
Files changed (80) hide show
  1. package/akanConfig/akanConfig.test.ts +75 -0
  2. package/akanConfig/akanConfig.ts +11 -1
  3. package/akanMcpContract.test.ts +28 -1
  4. package/akanMcpContract.ts +51 -3
  5. package/applicationBuildRunner.test.ts +36 -0
  6. package/applicationBuildRunner.ts +4 -3
  7. package/artifact/implicitRootLayout.ts +4 -2
  8. package/cloud/globalConfig.test.ts +34 -0
  9. package/cloud/globalConfig.ts +2 -2
  10. package/codeAgent/agent/AkanCodeEnv.ts +26 -0
  11. package/codeAgent/agent/AkanCodeServices.ts +5 -2
  12. package/codeAgent/agent/CodeAgent.ts +7 -0
  13. package/codeAgent/agent/CodeAgentGate.ts +156 -10
  14. package/codeAgent/agent/CodeAgentProxy.ts +5 -4
  15. package/codeAgent/agent/CodeAgentRpcHost.ts +3 -0
  16. package/codeAgent/agent/CodeAgentRpcListener.ts +102 -16
  17. package/codeAgent/agent/WorkspaceTrust.ts +200 -0
  18. package/codeAgent/agent/akanCodePaths.ts +8 -1
  19. package/codeAgent/agent/akanSystemPrompt.ts +4 -0
  20. package/codeAgent/agent/codeAgentContract.test.ts +119 -0
  21. package/codeAgent/agent/codeAgentHosts.ts +30 -0
  22. package/codeAgent/agent/codeAgentProxy.test.ts +53 -1
  23. package/codeAgent/agent/codeAgentRpcListener.test.ts +115 -13
  24. package/codeAgent/agent/stringArg.ts +1 -1
  25. package/codeAgent/skills/akan-agent/SKILL.md +8 -1
  26. package/codeAgent/skills/akan-code/SKILL.md +37 -11
  27. package/codeAgent/skills/akan-debug/SKILL.md +2 -1
  28. package/codeAgent/skills/akan-err/SKILL.md +2 -1
  29. package/codeAgent/skills/akan-query/SKILL.md +3 -2
  30. package/codeAgent/skills/akan-runtime/SKILL.md +3 -0
  31. package/codeAgent/tools/McpServerConfig.ts +6 -4
  32. package/codeAgent/tools/McpSignIn.ts +7 -7
  33. package/codeAgent/tools/McpTokenStore.ts +63 -16
  34. package/codeAgent/tools/McpToolPack.ts +8 -1
  35. package/codeAgent/tools/WebToolPack.ts +50 -6
  36. package/codeAgent/tools/akanCodePlugins.test.ts +70 -2
  37. package/codeAgent/tools/mcpOAuth.test.ts +50 -14
  38. package/codeAgent/tools/mcpServerConfig.test.ts +130 -1
  39. package/commandDecorators/command.ts +4 -1
  40. package/createTunnel.test.ts +200 -2
  41. package/createTunnel.ts +80 -6
  42. package/envSeed.test.ts +90 -0
  43. package/envSeed.ts +68 -0
  44. package/executors.test.ts +19 -1
  45. package/executors.ts +41 -2
  46. package/fileSys.test.ts +30 -2
  47. package/fileSys.ts +14 -0
  48. package/formSetterScanner.test.ts +44 -1
  49. package/formSetterScanner.ts +53 -8
  50. package/frontendBuild/csrArtifactBuilder.ts +7 -0
  51. package/frontendBuild/mcpAppViewBuilder.test.ts +63 -0
  52. package/frontendBuild/mcpAppViewBuilder.ts +7 -1
  53. package/frontendBuild/optionalPeers.test.ts +132 -0
  54. package/integration/ssrMemoryProbe.ts +6 -0
  55. package/lint/no-unguarded-endpoint.grit +5 -5
  56. package/maintainabilityReport.ts +1 -1
  57. package/mobile/desktopServerStage.test.ts +1 -0
  58. package/mobile/desktopServerStage.ts +3 -1
  59. package/mobile/nativeApp.test.ts +20 -5
  60. package/mobile/nativeApp.ts +4 -1
  61. package/mobile/nativeConfig.test.ts +209 -5
  62. package/mobile/nativeConfig.ts +127 -6
  63. package/mobile/nativePublicEnv.test.ts +2 -0
  64. package/mobile/nativePublicEnv.ts +13 -0
  65. package/package.json +3 -2
  66. package/shellOnlyEnv.test.ts +65 -0
  67. package/shellOnlyEnv.ts +54 -0
  68. package/signalMutationIndex.ts +22 -81
  69. package/sshHostKeys.ts +59 -0
  70. package/storeSource.ts +9 -8
  71. package/subspace.test.ts +10 -0
  72. package/subspaceClone.ts +3 -1
  73. package/subspaceConfig.ts +6 -0
  74. package/transforms/barrelImportsPlugin.ts +10 -4
  75. package/transforms/mcpAppPageTrimmer.test.ts +8 -2
  76. package/transforms/mcpAppPageTrimmer.ts +5 -20
  77. package/transforms/pureLazyMarker.ts +1 -1
  78. package/workflow/mutationScaffold.ts +35 -21
  79. package/workflow/plan.ts +28 -0
  80. package/workflow/sourceAst.ts +10 -48
@@ -1,4 +1,4 @@
1
- import { lowerlize } from "akanjs/common";
1
+ import { lowerlize, modelNames } from "akanjs/common";
2
2
  import type { Sys } from "../commandDecorators";
3
3
  import { GuardCatalog } from "./guardCatalog";
4
4
  import { type MutationActingUser, type MutationCall, MutationSource } from "./mutationSource";
@@ -53,20 +53,34 @@ export class MutationScaffold {
53
53
  }
54
54
 
55
55
  static reservedNamesOf(modelClassName: string) {
56
- const model = lowerlize(modelClassName);
57
- const prefixed = ["light", "create", "update", "remove", "view", "edit", "merge", "get", "load", "set", "reset"];
56
+ const names = modelNames(lowerlize(modelClassName));
58
57
  return new Set([
59
- model,
60
- ...[...prefixed, "new", "submit", "invalidate"].map((prefix) => `${prefix}${modelClassName}`),
61
- `create${modelClassName}InForm`,
62
- `update${modelClassName}InForm`,
63
- `load${modelClassName}Many`,
64
- `get${modelClassName}View`,
65
- `get${modelClassName}Edit`,
66
- `add${modelClassName}Files`,
67
- `check${modelClassName}Submitable`,
68
- `writeOn${modelClassName}`,
69
- ...["load", "restore", "discard"].map((prefix) => `${prefix}${modelClassName}FormDraft`),
58
+ names.model,
59
+ names.lightModel,
60
+ names.createModel,
61
+ names.updateModel,
62
+ names.removeModel,
63
+ names.viewModel,
64
+ names.editModel,
65
+ names.mergeModel,
66
+ names.getModel,
67
+ names.loadModel,
68
+ names.setModel,
69
+ names.resetModel,
70
+ names.newModel,
71
+ names.submitModel,
72
+ names.invalidateModel,
73
+ names.createModelInForm,
74
+ names.updateModelInForm,
75
+ names.loadModelMany,
76
+ names.getModelView,
77
+ names.getModelEdit,
78
+ names.addModelFiles,
79
+ names.checkModelSubmitable,
80
+ names.writeOnModel,
81
+ names.loadModelFormDraft,
82
+ names.restoreModelFormDraft,
83
+ names.discardModelFormDraft,
70
84
  ]);
71
85
  }
72
86
 
@@ -415,17 +429,17 @@ export class MutationScaffold {
415
429
  #parametersOf(service: ServiceSource, ref: string, method: string) {
416
430
  const declared = classMethodParameters(service.content, service.className, method);
417
431
  if (declared) return declared;
418
- const modelClassName = moduleComponentName(ref);
432
+ const names = modelNames(lowerlize(moduleComponentName(ref)));
419
433
  const generated: Record<string, { name: string; optional: boolean }[]> = {
420
- [`get${modelClassName}`]: [{ name: "id", optional: false }],
421
- [`load${modelClassName}`]: [{ name: "id", optional: true }],
422
- [`load${modelClassName}Many`]: [{ name: "ids", optional: false }],
423
- [`create${modelClassName}`]: [{ name: "data", optional: false }],
424
- [`update${modelClassName}`]: [
434
+ [names.getModel]: [{ name: "id", optional: false }],
435
+ [names.loadModel]: [{ name: "id", optional: true }],
436
+ [names.loadModelMany]: [{ name: "ids", optional: false }],
437
+ [names.createModel]: [{ name: "data", optional: false }],
438
+ [names.updateModel]: [
425
439
  { name: "id", optional: false },
426
440
  { name: "data", optional: false },
427
441
  ],
428
- [`remove${modelClassName}`]: [{ name: "id", optional: false }],
442
+ [names.removeModel]: [{ name: "id", optional: false }],
429
443
  };
430
444
  return generated[method] ?? null;
431
445
  }
package/workflow/plan.ts CHANGED
@@ -19,6 +19,24 @@ import { addFieldUiPolicyForType } from "./uiPolicy";
19
19
 
20
20
  const surfaceModes = new Set<WorkflowSurfaceMode>(["infer", "include", "skip"]);
21
21
 
22
+ const sysNamePattern = /^[a-z0-9][a-zA-Z0-9_-]*$/;
23
+ const identifierPattern = /^_{0,2}[a-zA-Z][a-zA-Z0-9_-]*$/;
24
+ //? These inputs become path segments, identifiers and command arguments, so none may carry a separator or a dot.
25
+ const nameInputPatterns: Record<string, RegExp> = {
26
+ app: sysNamePattern,
27
+ module: identifierPattern,
28
+ scalar: identifierPattern,
29
+ field: identifierPattern,
30
+ slice: identifierPattern,
31
+ mutation: identifierPattern,
32
+ interaction: identifierPattern,
33
+ };
34
+
35
+ export const isWorkflowSysName = (value: unknown): value is string =>
36
+ typeof value === "string" && sysNamePattern.test(value);
37
+ export const isWorkflowModuleName = (value: unknown): value is string =>
38
+ typeof value === "string" && identifierPattern.test(value);
39
+
22
40
  const parseStringList = (value: unknown) => {
23
41
  if (Array.isArray(value)) {
24
42
  const values = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
@@ -365,6 +383,16 @@ export const createWorkflowPlan = (spec: WorkflowSpec, rawInputs: Record<string,
365
383
  });
366
384
  continue;
367
385
  }
386
+ const namePattern = nameInputPatterns[name];
387
+ if (namePattern && typeof value === "string" && !namePattern.test(value)) {
388
+ diagnostics.push({
389
+ severity: "error",
390
+ code: "workflow-input-invalid",
391
+ input: name,
392
+ message: `Workflow ${spec.name} input "${name}" must be a plain name without dots, slashes or spaces, not ${JSON.stringify(value)}.`,
393
+ });
394
+ continue;
395
+ }
368
396
  if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
369
397
  diagnostics.push({
370
398
  severity: "error",
@@ -1,50 +1,12 @@
1
+ import { createAst } from "@akanjs/language";
1
2
  import ts from "typescript";
2
3
 
3
- export const sourceFileFor = (fileName: string, content: string) =>
4
- ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
5
-
6
- export const nodeName = (node: ts.PropertyName | ts.BindingName | undefined) => {
7
- if (!node) return null;
8
- if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
9
- return null;
10
- };
11
-
12
- export const propertyName = (node: ts.ObjectLiteralElementLike) =>
13
- ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node) || ts.isMethodDeclaration(node)
14
- ? nodeName(node.name)
15
- : null;
16
-
17
- export const expressionName = (expression: ts.Expression): string | null => {
18
- if (ts.isIdentifier(expression)) return expression.text;
19
- if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
20
- if (ts.isCallExpression(expression)) return expressionName(expression.expression);
21
- if (ts.isAsExpression(expression)) return expressionName(expression.expression);
22
- return null;
23
- };
24
-
25
- export const callExpressionName = (node: ts.CallExpression) =>
26
- ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : expressionName(node.expression);
27
-
28
- export const firstObjectReturnedByArrow = (node: ts.Node): ts.ObjectLiteralExpression | null => {
29
- if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) return null;
30
- if (ts.isObjectLiteralExpression(node.body)) return node.body;
31
- if (ts.isParenthesizedExpression(node.body) && ts.isObjectLiteralExpression(node.body.expression)) {
32
- return node.body.expression;
33
- }
34
- if (!ts.isBlock(node.body)) return null;
35
- for (const statement of node.body.statements) {
36
- if (ts.isReturnStatement(statement) && statement.expression && ts.isObjectLiteralExpression(statement.expression)) {
37
- return statement.expression;
38
- }
39
- }
40
- return null;
41
- };
42
-
43
- const isViaCall = (expression: ts.Expression) =>
44
- ts.isCallExpression(expression) && expressionName(expression.expression) === "via";
45
-
46
- export const heritageCall = (node: ts.ClassDeclaration) => {
47
- const heritage = node.heritageClauses?.flatMap((clause) => [...clause.types]) ?? [];
48
- const expression = heritage.find((clause) => isViaCall(clause.expression))?.expression;
49
- return expression && ts.isCallExpression(expression) ? expression : null;
50
- };
4
+ export const {
5
+ sourceFileFor,
6
+ nodeName,
7
+ propertyName,
8
+ expressionName,
9
+ callExpressionName,
10
+ firstObjectReturnedByArrow,
11
+ heritageCall,
12
+ } = createAst(ts);