@intelligo-dev/cli 1.0.0-beta.1 → 1.0.0-beta.2

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.
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { existsSync, readFileSync } from "node:fs";
11
11
  import path from "node:path";
12
- import { readCatalogue } from "./add.js";
12
+ import { readCatalogue, substitute } from "./add.js";
13
13
  import { hashContents, readManifest } from "../manifest.js";
14
14
  export function upgradeCheck(options) {
15
15
  const manifest = readManifest(options.appRoot);
@@ -36,10 +36,13 @@ export function upgradeCheck(options) {
36
36
  }
37
37
  const localHash = hashContents(readFileSync(abs, "utf8"));
38
38
  const customized = localHash !== file.hash;
39
+ // Compare against the template as it would be written for THIS
40
+ // app: the recorded hash is of substituted content, so the raw
41
+ // template never matched for any file carrying a placeholder.
39
42
  const templateRel = targetToTemplate.get(file.path);
40
43
  let templateChanged = false;
41
44
  if (templateRel) {
42
- const templateHash = hashContents(readFileSync(path.join(options.templatesDir, templateRel), "utf8"));
45
+ const templateHash = hashContents(substitute(readFileSync(path.join(options.templatesDir, templateRel), "utf8"), entry.variables));
43
46
  templateChanged = templateHash !== file.hash;
44
47
  }
45
48
  report.items.push({
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade-check.js","sourceRoot":"","sources":["../../src/commands/upgrade-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upgrade-check.js","sourceRoot":"","sources":["../../src/commands/upgrade-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAiB,MAAM,gBAAgB,CAAC;AA2B3E,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,QAAQ,GAAoB,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,MAAM,GAAkB;QAC5B,gBAAgB,EAAE,EAAE;QACpB,eAAe,EAAE,EAAE;QACnB,KAAK,EAAE,EAAE;KACV,CAAC;IAEF,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,eAAe,CAAC;QACzD,IAAI,IAAI;YAAE,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;QAEjE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CACvD,CAAC;QAEF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBAClE,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YAC1D,MAAM,UAAU,GAAG,SAAS,KAAK,IAAI,CAAC,IAAI,CAAC;YAE3C,+DAA+D;YAC/D,+DAA+D;YAC/D,8DAA8D;YAC9D,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpD,IAAI,eAAe,GAAG,KAAK,CAAC;YAC5B,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,YAAY,GAAG,YAAY,CAC/B,UAAU,CACR,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,EAClE,KAAK,CAAC,SAAS,CAChB,CACF,CAAC;gBACF,eAAe,GAAG,YAAY,KAAK,IAAI,CAAC,IAAI,CAAC;YAC/C,CAAC;YAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAChB,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,eAAe;oBACpB,CAAC,CAAC,UAAU;wBACV,CAAC,CAAC,UAAU;wBACZ,CAAC,CAAC,UAAU;oBACd,CAAC,CAAC,UAAU;wBACV,CAAC,CAAC,YAAY;wBACd,CAAC,CAAC,SAAS;aAChB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,CAAgB;IAClD,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,mDAAmD,CAAC;IAC7D,CAAC;IAED,MAAM,KAAK,GAAyC;QAClD,OAAO,EAAE,cAAc;QACvB,QAAQ,EAAE,qDAAqD;QAC/D,QAAQ,EAAE,wDAAwD;QAClE,UAAU,EAAE,8BAA8B;QAC1C,OAAO,EAAE,kBAAkB;KAC5B,CAAC;IAEF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtE,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,CAAC,IAAI,CACR,MAAM,IAAI,MAAM,KAAK,SAAS;YAC5B,CAAC,CAAC,GAAG,OAAO,KAAK,SAAS,MAAM,MAAM,EAAE;YACxC,CAAC,CAAC,GAAG,OAAO,KAAK,SAAS,EAAE,CAC/B,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;YAChE,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,CAAgB;IACnD,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC"}
@@ -25,6 +25,13 @@ export type GeneratedFile = {
25
25
  export type FeatureEntry = {
26
26
  templateVersion: string;
27
27
  files: GeneratedFile[];
28
+ /**
29
+ * Placeholder substitutions the files were generated with
30
+ * (`__APP_NAME__` → "acme"). Recorded so an upgrade check can hash the
31
+ * template *as it would be written for this app* — hashing the raw
32
+ * template made every substituted file look permanently outdated.
33
+ */
34
+ variables?: Record<string, string>;
28
35
  };
29
36
  export type Manifest = {
30
37
  schemaVersion: 1;
@@ -54,5 +61,5 @@ export declare function inspectFeature(appRoot: string, entry: FeatureEntry): Fi
54
61
  * template version, and the recorded hashes must then describe what is
55
62
  * actually on disk.
56
63
  */
57
- export declare function recordFeature(manifest: Manifest, feature: string, templateVersion: string, files: GeneratedFile[]): Manifest;
64
+ export declare function recordFeature(manifest: Manifest, feature: string, templateVersion: string, files: GeneratedFile[], variables?: Record<string, string>): Manifest;
58
65
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,eAAO,MAAM,aAAa,4BAA4B,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAC1B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,aAAa,EAAE,CAAC,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,QAAQ,CAEhE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAI7D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAKvE;AAED;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,UAAU,CAS5E;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,GAClB,UAAU,EAAE,CAEd;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,aAAa,EAAE,GACrB,QAAQ,CAQV"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,eAAO,MAAM,aAAa,4BAA4B,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAC1B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,aAAa,EAAE,CAAC,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,QAAQ,CAEhE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAI7D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAKvE;AAED;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,UAAU,CAS5E;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,GAClB,UAAU,EAAE,CAEd;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,aAAa,EAAE,EACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,QAAQ,CAUV"}
package/dist/manifest.js CHANGED
@@ -53,12 +53,14 @@ export function inspectFeature(appRoot, entry) {
53
53
  * template version, and the recorded hashes must then describe what is
54
54
  * actually on disk.
55
55
  */
56
- export function recordFeature(manifest, feature, templateVersion, files) {
56
+ export function recordFeature(manifest, feature, templateVersion, files, variables) {
57
57
  return {
58
58
  ...manifest,
59
59
  features: {
60
60
  ...manifest.features,
61
- [feature]: { templateVersion, files },
61
+ [feature]: variables
62
+ ? { templateVersion, files, variables }
63
+ : { templateVersion, files },
62
64
  },
63
65
  };
64
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAoBvD,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,gBAAwB;IACpD,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAa,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,QAAkB;IAC/D,aAAa,CACX,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACjC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACzC,CAAC;AACJ,CAAC;AAeD,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAmB;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEnE,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAmB;IAEnB,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAkB,EAClB,OAAe,EACf,eAAuB,EACvB,KAAsB;IAEtB,OAAO;QACL,GAAG,QAAQ;QACX,QAAQ,EAAE;YACR,GAAG,QAAQ,CAAC,QAAQ;YACpB,CAAC,OAAO,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE;SACtC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,yBAAyB,CAAC;AA2BvD,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,gBAAwB;IACpD,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAa,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,QAAkB;IAC/D,aAAa,CACX,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACjC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACzC,CAAC;AACJ,CAAC;AAeD,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAmB;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEnE,MAAM,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY;KAC1D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAmB;IAEnB,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAkB,EAClB,OAAe,EACf,eAAuB,EACvB,KAAsB,EACtB,SAAkC;IAElC,OAAO;QACL,GAAG,QAAQ;QACX,QAAQ,EAAE;YACR,GAAG,QAAQ,CAAC,QAAQ;YACpB,CAAC,OAAO,CAAC,EAAE,SAAS;gBAClB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE;gBACvC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE;SAC/B;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelligo-dev/cli",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Tables this application owns.
3
+ *
4
+ * Everything the framework needs — users, workspaces, members, plans,
5
+ * subscriptions, credits, executions, audit events, notifications,
6
+ * conversations, documents — is `@intelligo-dev/core`'s and arrives
7
+ * with its own migrations. Define your product's tables here, scope
8
+ * them to a workspace, and reference framework tables by import:
9
+ *
10
+ * import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
11
+ * import { organization, users } from "@intelligo-dev/core/db/schema";
12
+ *
13
+ * export const reports = pgTable("reports", {
14
+ * id: text("id").primaryKey(),
15
+ * workspaceId: text("workspace_id")
16
+ * .notNull()
17
+ * .references(() => organization.id, { onDelete: "cascade" }),
18
+ * createdBy: text("created_by").references(() => users.id),
19
+ * createdAt: timestamp("created_at").defaultNow().notNull(),
20
+ * });
21
+ *
22
+ * Then `pnpm db:generate` writes the migration into ./drizzle and
23
+ * `pnpm db:migrate` applies it after the framework's chain.
24
+ *
25
+ * Intelligo never reads these tables (ADR-0004: every table has one
26
+ * owner), so their shape is entirely yours.
27
+ */
28
+
29
+ export {};
@@ -0,0 +1,42 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+ import { config } from "dotenv";
3
+
4
+ // drizzle-kit does not load env files; Next.js does. Same precedence.
5
+ config({ path: ".env.local" });
6
+ config({ path: ".env" });
7
+
8
+ /**
9
+ * Your application's migration chain — and only yours.
10
+ *
11
+ * The framework's tables (users, workspaces, plans, credits, executions,
12
+ * …) arrive inside `@intelligo-dev/core` together with their migration
13
+ * history, and `intelligo migrate` applies that chain. This config
14
+ * covers the tables you own: list their schema files here, generate
15
+ * with `pnpm db:generate`, apply with `pnpm db:migrate`.
16
+ *
17
+ * Two chains, one database, so they record themselves in different
18
+ * tables: the framework in drizzle's default `__drizzle_migrations`,
19
+ * yours in `__app_migrations`. Do not merge them — drizzle-kit applies
20
+ * by timestamp, and a framework migration published after you
21
+ * generated one of yours would be skipped silently.
22
+ *
23
+ * Your schema may reference framework tables (`references(() =>
24
+ * users.id)`); drizzle-kit emits the foreign key by name and does not
25
+ * try to create the framework's table.
26
+ *
27
+ * Do not point `drizzle-kit push` at this config: push diffs the whole
28
+ * database against the schema it sees, and this config deliberately
29
+ * sees only yours.
30
+ */
31
+ export default defineConfig({
32
+ dialect: "postgresql",
33
+ schema: ["./lib/db/schema.ts"],
34
+ out: "./drizzle",
35
+ migrations: { table: "__app_migrations", schema: "drizzle" },
36
+ // The framework's columns are snake_case in Postgres and camelCase in
37
+ // TypeScript. Keep the same convention so joins read naturally.
38
+ casing: "snake_case",
39
+ dbCredentials: {
40
+ url: process.env.DATABASE_URL!,
41
+ },
42
+ });
@@ -1,5 +1,8 @@
1
1
  # Required — the app will not boot without these.
2
2
  DATABASE_URL=
3
+ # Driver is chosen from the URL (Neon hosts get the WebSocket driver,
4
+ # everything else node-postgres). Force it with pg | neon-serverless.
5
+ # INTELLIGO_DB_DRIVER=
3
6
  BETTER_AUTH_SECRET=
4
7
  NEXT_PUBLIC_APP_URL=http://localhost:3000
5
8
 
@@ -8,6 +11,14 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000
8
11
  # variable is here so CLI tooling can check it without booting the app.
9
12
  INTELLIGO_BILLING_PRODUCT=__APP_SLUG__
10
13
 
14
+ # Stripe: the webhook endpoint at /api/webhooks/stripe verifies with this.
15
+ STRIPE_SECRET_KEY=
16
+ STRIPE_WEBHOOK_SECRET=
17
+
18
+ # Bearer token your scheduler sends to /api/cron/maintenance
19
+ # (`intelligo add maintenance`). At least 32 characters.
20
+ CRON_SECRET=
21
+
11
22
  # Comma-separated emails allowed to reach the Intelligo admin console.
12
23
  # Closed by default: with no value, nobody is a platform admin.
13
24
  PLATFORM_ADMIN_EMAILS=
@@ -14,15 +14,17 @@ import "server-only";
14
14
  */
15
15
 
16
16
  import {
17
- checkQuota,
17
+ reserveQuota,
18
18
  recordTokenUsage,
19
19
  releaseReservation,
20
+ findSettlementByRequestId,
20
21
  } from "@intelligo-dev/billing";
21
22
  import {
22
23
  registerProductFeatures,
23
24
  registerProductPlans,
24
25
  setDefaultProductSlug,
25
26
  } from "@intelligo-dev/billing/plans";
27
+ import { assertEnv } from "@intelligo-dev/core/env";
26
28
  import { createExecutions } from "@intelligo-dev/executions";
27
29
 
28
30
  import { PLANS, FEATURES } from "./plans";
@@ -44,6 +46,11 @@ export function composeIntelligo(): void {
44
46
  if (composed) return;
45
47
  composed = true;
46
48
 
49
+ // Fail on the first request rather than on the first query: a
50
+ // missing DATABASE_URL or auth secret is a configuration error, not
51
+ // something to discover deep inside a handler.
52
+ assertEnv();
53
+
47
54
  setDefaultProductSlug(PRODUCT_SLUG);
48
55
  registerProductPlans(PRODUCT_SLUG, PLANS);
49
56
  registerProductFeatures(PRODUCT_SLUG, FEATURES);
@@ -54,9 +61,10 @@ export const executions = createExecutions({
54
61
  // Passing requestId makes admission atomic: the worst-case cost is
55
62
  // reserved in the same transaction that reads the balance, so
56
63
  // concurrent requests cannot all pass.
57
- const quota = await checkQuota(workspaceId, { modelId: model, requestId });
64
+ const quota = await reserveQuota(workspaceId, { modelId: model, requestId });
58
65
  return {
59
66
  allowed: quota.allowed,
67
+ code: quota.code,
60
68
  reason: quota.reason,
61
69
  estimatedMnt: quota.estimatedMnt,
62
70
  usingTrialCredits: quota.usingTrialCredits,
@@ -64,7 +72,9 @@ export const executions = createExecutions({
64
72
  },
65
73
 
66
74
  async settleUsage(s) {
67
- await recordTokenUsage({
75
+ // Returns what was charged and which pool funded it; the lifecycle
76
+ // records chargedMnt on the execution row.
77
+ return recordTokenUsage({
68
78
  workspaceId: s.workspaceId,
69
79
  userId: s.userId ?? "",
70
80
  model: s.model ?? "unknown",
@@ -81,4 +91,9 @@ export const executions = createExecutions({
81
91
  async releaseHold({ requestId }) {
82
92
  await releaseReservation(requestId);
83
93
  },
94
+
95
+ // Lets executions.reconcile() tell a settling row whose charge
96
+ // committed from one whose charge never happened.
97
+ findSettlement: ({ workspaceId, requestId }) =>
98
+ findSettlementByRequestId(workspaceId, requestId),
84
99
  });
@@ -7,7 +7,10 @@
7
7
  "dev": "next dev --turbopack --port 3000",
8
8
  "build": "next build",
9
9
  "start": "next start --port 3000",
10
- "type-check": "tsc --noEmit"
10
+ "type-check": "tsc --noEmit",
11
+ "db:generate": "drizzle-kit generate",
12
+ "db:migrate": "intelligo migrate && drizzle-kit migrate",
13
+ "db:check": "intelligo migrate --check"
11
14
  },
12
15
  "dependencies": {
13
16
  "@intelligo-dev/admin": "__INTELLIGO_DEP__",
@@ -16,10 +19,12 @@
16
19
  "@intelligo-dev/billing": "__INTELLIGO_DEP__",
17
20
  "@intelligo-dev/core": "__INTELLIGO_DEP__",
18
21
  "@intelligo-dev/executions": "__INTELLIGO_DEP__",
22
+ "@intelligo-dev/jobs": "__INTELLIGO_DEP__",
19
23
  "@intelligo-dev/ui": "__INTELLIGO_DEP__",
20
24
  "@radix-ui/react-slot": "^1.2.4",
21
25
  "class-variance-authority": "^0.7.1",
22
26
  "clsx": "^2.1.1",
27
+ "drizzle-orm": "^0.45.1",
23
28
  "lucide-react": "^0.563.0",
24
29
  "next": "^16.2.3",
25
30
  "next-intl": "^4.9.2",
@@ -31,10 +36,13 @@
31
36
  "tailwind-merge": "^3.4.0"
32
37
  },
33
38
  "devDependencies": {
39
+ "@intelligo-dev/cli": "__INTELLIGO_DEP__",
34
40
  "@tailwindcss/postcss": "^4",
35
41
  "@types/node": "^22.0.0",
36
42
  "@types/react": "^19.0.0",
37
43
  "@types/react-dom": "^19.0.0",
44
+ "dotenv": "^16.4.7",
45
+ "drizzle-kit": "^0.31.8",
38
46
  "tailwindcss": "^4",
39
47
  "tw-animate-css": "^1.4.0",
40
48
  "typescript": "^5.7.2"
@@ -37,7 +37,15 @@ export const PLANS: Record<string, PlanConfig> = {
37
37
  },
38
38
  };
39
39
 
40
- /** Which plans grant which feature. An unregistered feature is denied. */
40
+ /**
41
+ * Which plans grant which feature. An unregistered feature is denied —
42
+ * a registry item whose `featureKey` is missing here returns 403 on
43
+ * every request, so add the key when you install the item.
44
+ */
41
45
  export const FEATURES: Record<string, readonly string[]> = {
42
46
  assistant: ["free", "pro"],
47
+ // POST /api/chat (the `chat` registry item). Every plan, so a clean
48
+ // install can chat with no configuration; tighten to ["pro"] to put
49
+ // chat behind a paywall.
50
+ chat: ["free", "pro"],
43
51
  };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Stripe webhook endpoint.
3
+ *
4
+ * Point Stripe at POST /api/webhooks/stripe and set
5
+ * STRIPE_WEBHOOK_SECRET. The handler verifies the signature, records
6
+ * every event in `finance_events` before processing it, runs the
7
+ * built-in handlers (checkout completed, invoice paid / failed,
8
+ * subscription updated / deleted), and answers 500 on a handler error
9
+ * so Stripe retries — see `createStripeWebhookHandler` in
10
+ * `@intelligo-dev/billing` for the exact contract.
11
+ *
12
+ * Pass `onEvent` to react to event types the framework does not.
13
+ */
14
+
15
+ import { createStripeWebhookHandler } from "@intelligo-dev/billing";
16
+
17
+ import { composeIntelligo } from "@/lib/intelligo";
18
+
19
+ const handler = createStripeWebhookHandler();
20
+
21
+ export async function POST(request: Request) {
22
+ composeIntelligo();
23
+ return handler(request);
24
+ }
@@ -4,10 +4,12 @@
4
4
  * Theme provider — mounted by `app/[locale]/layout.tsx` so any theme
5
5
  * switcher (e.g. the app-shell item's user menu) actually works.
6
6
  *
7
- * The same file ships with the `app-shell` registry item at the same
8
- * path; installing app-shell simply overwrites this copy with an
9
- * identical one. Defaults: class-attribute strategy (Tailwind's `.dark`
10
- * tokens), system preference honored, no transition flash on switch.
7
+ * The `app-shell` registry item ships its own provider at this same
8
+ * path; installing app-shell (with --overwrite) replaces this starter
9
+ * copy, and the manifest will report the file as customized from then
10
+ * on — that is expected. Defaults: class-attribute strategy (Tailwind's
11
+ * `.dark` tokens), system preference honored, no transition flash on
12
+ * switch.
11
13
  */
12
14
 
13
15
  import { ThemeProvider as NextThemesProvider } from "next-themes";
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Maintenance route — the loop the framework's comments assume exists.
3
+ *
4
+ * Mount a scheduler at GET /api/cron/maintenance with
5
+ * `Authorization: Bearer $CRON_SECRET` every few minutes. On Vercel,
6
+ * add an entry to `crons` in vercel.json with this path and a
7
+ * five-minute schedule (`0-59/5 * * * *`); Vercel sends the header
8
+ * itself.
9
+ *
10
+ * What one run does, and why each step exists:
11
+ *
12
+ * - **Reconcile stale executions.** A row still `running` or `settling`
13
+ * past the chat route's `maxDuration` is usage nobody finished
14
+ * accounting for. `executions.reconcile` confirms a charge the
15
+ * ledger already holds, re-runs settlement from the recorded usage,
16
+ * or fails an abandoned run and releases its hold.
17
+ * - **Drop expired reservations.** Admission already ignores them; this
18
+ * is table hygiene.
19
+ * - **Drop old rate-limit buckets.** Same.
20
+ * - **Expire trials and send reminders.** `hasActiveTrial` checks the
21
+ * end date inline, so admission is safe without this — feature
22
+ * access and the reminder emails are what it drives.
23
+ * - **Prune finished jobs** older than a week.
24
+ *
25
+ * Draining the job queue is NOT done here: `drain` needs your handlers,
26
+ * so give it its own route (or call it below once you have some).
27
+ *
28
+ * This file is yours. Add product maintenance to it freely.
29
+ */
30
+
31
+ import { timingSafeEqual } from "node:crypto";
32
+
33
+ import {
34
+ cleanupExpiredReservations,
35
+ cleanupRateLimitEntries,
36
+ processTrialExpirations,
37
+ } from "@intelligo-dev/billing";
38
+ import { findStaleExecutions } from "@intelligo-dev/executions";
39
+ import { pruneJobs } from "@intelligo-dev/jobs";
40
+ import { createLogger } from "@intelligo-dev/core/logger";
41
+
42
+ import { composeIntelligo, executions } from "@/lib/intelligo";
43
+
44
+ const log = createLogger("cron.maintenance");
45
+
46
+ export const maxDuration = 60;
47
+
48
+ /** Executions older than this and still non-terminal are reconciled. */
49
+ const STALE_AFTER_MS = 10 * 60_000;
50
+ /** Finished jobs older than this are pruned. */
51
+ const PRUNE_JOBS_AFTER_MS = 7 * 24 * 60 * 60_000;
52
+
53
+ /** Constant-time bearer comparison; length is compared too. */
54
+ function bearerMatches(header: string | null, secret: string): boolean {
55
+ if (!header?.startsWith("Bearer ")) return false;
56
+ const token = Buffer.from(header.slice("Bearer ".length));
57
+ const expected = Buffer.from(secret);
58
+ if (token.length !== expected.length) return false;
59
+ return timingSafeEqual(token, expected);
60
+ }
61
+
62
+ export async function GET(request: Request) {
63
+ const secret = process.env.CRON_SECRET;
64
+ if (!secret || secret.length < 32) {
65
+ log.error("CRON_SECRET missing or shorter than 32 chars — refusing");
66
+ return new Response("Forbidden", { status: 403 });
67
+ }
68
+ if (!bearerMatches(request.headers.get("authorization"), secret)) {
69
+ return new Response("Unauthorized", { status: 401 });
70
+ }
71
+
72
+ composeIntelligo();
73
+ const errors: string[] = [];
74
+ const step = async <T>(name: string, run: () => Promise<T>): Promise<T | null> => {
75
+ try {
76
+ return await run();
77
+ } catch (error) {
78
+ const message = error instanceof Error ? error.message : String(error);
79
+ log.error(`${name} failed`, { error: message });
80
+ errors.push(`${name}: ${message}`);
81
+ return null;
82
+ }
83
+ };
84
+
85
+ const stale = await step("findStaleExecutions", () =>
86
+ findStaleExecutions(new Date(Date.now() - STALE_AFTER_MS))
87
+ );
88
+ const reconciled: Record<string, number> = {};
89
+ for (const row of stale ?? []) {
90
+ const result = await step(`reconcile ${row.id}`, () =>
91
+ executions.reconcile(row.id, { abandonRunningAfterMs: STALE_AFTER_MS })
92
+ );
93
+ if (result) reconciled[result.action] = (reconciled[result.action] ?? 0) + 1;
94
+ }
95
+
96
+ const summary = {
97
+ staleExecutions: stale?.length ?? 0,
98
+ reconciled,
99
+ expiredReservationsDeleted: await step("cleanupExpiredReservations", cleanupExpiredReservations),
100
+ rateLimitEntriesDeleted: await step("cleanupRateLimitEntries", cleanupRateLimitEntries),
101
+ trials: await step("processTrialExpirations", processTrialExpirations),
102
+ jobsPruned: await step("pruneJobs", () =>
103
+ pruneJobs(new Date(Date.now() - PRUNE_JOBS_AFTER_MS))
104
+ ),
105
+ errors,
106
+ };
107
+
108
+ log.info("Maintenance run", summary);
109
+ return Response.json(summary, { status: errors.length ? 207 : 200 });
110
+ }
@@ -8,11 +8,11 @@
8
8
  "target": "app/usage/page.tsx"
9
9
  }
10
10
  ],
11
- "deprecated": "Superseded by the `usage` registry item \u2014 install it with the shadcn CLI from the Intelligo registry instead; it ships a full page, components and thin actions as consumer-owned source."
11
+ "deprecated": "Superseded by the `usage` registry item — install it with the shadcn CLI from the Intelligo registry instead; it ships a full page, components and thin actions as consumer-owned source."
12
12
  },
13
13
  "app-scaffold": {
14
- "templateVersion": "1.4.0",
15
- "description": "A new application: composition root, plans, an assistant route, config, next-intl i18n plumbing, and a registry-consuming shadcn/Tailwind setup",
14
+ "templateVersion": "1.7.0",
15
+ "description": "A new application: composition root, plans, an assistant route, a Stripe webhook endpoint, config, a drizzle config for the tables you own, next-intl i18n plumbing, and a registry-consuming shadcn/Tailwind setup",
16
16
  "files": [
17
17
  {
18
18
  "template": "app-scaffold/package.json.tpl",
@@ -89,6 +89,18 @@
89
89
  {
90
90
  "template": "app-scaffold/messages-en-app.json.tpl",
91
91
  "target": "messages/en/app.json"
92
+ },
93
+ {
94
+ "template": "app-scaffold/drizzle.config.ts.tpl",
95
+ "target": "drizzle.config.ts"
96
+ },
97
+ {
98
+ "template": "app-scaffold/db-schema.ts.tpl",
99
+ "target": "lib/db/schema.ts"
100
+ },
101
+ {
102
+ "template": "app-scaffold/stripe-webhook-route.ts.tpl",
103
+ "target": "app/api/webhooks/stripe/route.ts"
92
104
  }
93
105
  ]
94
106
  },
@@ -101,7 +113,7 @@
101
113
  "target": "app/billing/page.tsx"
102
114
  }
103
115
  ],
104
- "deprecated": "Superseded by the `pricing` and `billing-settings` registry items \u2014 install them with the shadcn CLI from the Intelligo registry instead."
116
+ "deprecated": "Superseded by the `pricing` and `billing-settings` registry items — install them with the shadcn CLI from the Intelligo registry instead."
105
117
  },
106
118
  "admin-page": {
107
119
  "templateVersion": "1.0.0",
@@ -112,5 +124,15 @@
112
124
  "target": "app/admin/page.tsx"
113
125
  }
114
126
  ]
127
+ },
128
+ "maintenance": {
129
+ "templateVersion": "1.0.0",
130
+ "description": "A CRON_SECRET-gated GET /api/cron/maintenance that reconciles stale executions, drops expired reservations and rate-limit buckets, expires trials and prunes old jobs — schedule it every few minutes",
131
+ "files": [
132
+ {
133
+ "template": "maintenance/maintenance-route.ts.tpl",
134
+ "target": "app/api/cron/maintenance/route.ts"
135
+ }
136
+ ]
115
137
  }
116
138
  }