@trydreamstate/cli 1.3.2 → 1.3.4

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 (91) hide show
  1. package/README.md +36 -16
  2. package/agent-plugin/.claude-plugin/plugin.json +1 -1
  3. package/agent-plugin/.codex-plugin/plugin.json +1 -1
  4. package/agent-plugin/hooks/approve-readonly.sh +15 -0
  5. package/agent-plugin/hooks/hooks.json +17 -0
  6. package/agent-plugin/skills/crm/SKILL.md +2 -2
  7. package/agent-plugin/skills/diagnostics/SKILL.md +3 -1
  8. package/agent-plugin/skills/dreamstate/SKILL.md +15 -5
  9. package/agent-plugin/skills/first-win/SKILL.md +2 -2
  10. package/agent-plugin/skills/inbox-replies/SKILL.md +3 -1
  11. package/agent-plugin/skills/knowledge/SKILL.md +3 -5
  12. package/agent-plugin/skills/linkedin-posts/SKILL.md +3 -1
  13. package/agent-plugin/skills/linkedin-warm-signals/SKILL.md +2 -2
  14. package/agent-plugin/skills/list-building/SKILL.md +5 -10
  15. package/agent-plugin/skills/list-building/column-authoring.md +1 -1
  16. package/agent-plugin/skills/list-building/sourcing.md +2 -2
  17. package/agent-plugin/skills/outreach/SKILL.md +2 -2
  18. package/agent-plugin/skills/outreach/steps-and-timing.md +1 -1
  19. package/agent-plugin/skills/plays/SKILL.md +3 -3
  20. package/agent-plugin/skills/reddit-posts/SKILL.md +3 -1
  21. package/agent-plugin/skills/skill-authoring/SKILL.md +3 -1
  22. package/agent-plugin/skills/social/SKILL.md +4 -6
  23. package/agent-plugin/skills/visibility/SKILL.md +2 -6
  24. package/agent-plugin/skills/workflows/SKILL.md +3 -3
  25. package/agent-plugin/skills/workflows/lifecycle-and-enrollment.md +1 -1
  26. package/agent-plugin/skills/worksheet-tidy/SKILL.md +3 -1
  27. package/agent-plugin/skills/writing/SKILL.md +3 -5
  28. package/agent-plugin/skills/x-posts/SKILL.md +3 -1
  29. package/dist/agentGuidance.generated.json +35 -0
  30. package/dist/capabilityPosture.d.ts +12 -0
  31. package/dist/capabilityPosture.d.ts.map +1 -0
  32. package/dist/capabilityPosture.js +39 -0
  33. package/dist/client.d.ts +33 -2
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +155 -64
  36. package/dist/commandTable.d.ts +26 -0
  37. package/dist/commandTable.d.ts.map +1 -0
  38. package/dist/commandTable.js +54 -0
  39. package/dist/commands/sequences.d.ts +3 -2
  40. package/dist/commands/sequences.d.ts.map +1 -1
  41. package/dist/commands/sequences.js +17 -34
  42. package/dist/commands/shared.d.ts +4 -26
  43. package/dist/commands/shared.d.ts.map +1 -1
  44. package/dist/commands/shared.js +136 -158
  45. package/dist/commands/workflows.d.ts +3 -2
  46. package/dist/commands/workflows.d.ts.map +1 -1
  47. package/dist/commands/workflows.js +17 -39
  48. package/dist/errors.d.ts +74 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +84 -0
  51. package/dist/gatedRun.d.ts +79 -0
  52. package/dist/gatedRun.d.ts.map +1 -0
  53. package/dist/gatedRun.js +346 -0
  54. package/dist/generated/packVerbs.generated.json +492 -0
  55. package/dist/generated/runContract.generated.d.ts +4 -0
  56. package/dist/generated/runContract.generated.d.ts.map +1 -0
  57. package/dist/generated/runContract.generated.js +7 -0
  58. package/dist/idempotency.d.ts +38 -0
  59. package/dist/idempotency.d.ts.map +1 -0
  60. package/dist/idempotency.js +106 -0
  61. package/dist/index.d.ts +22 -35
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +405 -378
  64. package/dist/registryManifest.d.ts +6 -0
  65. package/dist/registryManifest.d.ts.map +1 -0
  66. package/dist/registryManifest.js +66 -0
  67. package/dist/runContract.d.ts +10 -0
  68. package/dist/runContract.d.ts.map +1 -0
  69. package/dist/runContract.js +13 -0
  70. package/dist/runWait.d.ts +2 -2
  71. package/dist/runWait.d.ts.map +1 -1
  72. package/dist/runWait.js +3 -14
  73. package/dist/schemaCoercion.d.ts +20 -0
  74. package/dist/schemaCoercion.d.ts.map +1 -0
  75. package/dist/schemaCoercion.js +123 -0
  76. package/dist/skillInstaller.d.ts +40 -0
  77. package/dist/skillInstaller.d.ts.map +1 -1
  78. package/dist/skillInstaller.js +108 -9
  79. package/dist/tableFilePreview.d.ts +14 -0
  80. package/dist/tableFilePreview.d.ts.map +1 -1
  81. package/dist/tableFilePreview.js +19 -8
  82. package/dist/tableFileReader.d.ts +26 -0
  83. package/dist/tableFileReader.d.ts.map +1 -0
  84. package/dist/tableFileReader.js +108 -0
  85. package/dist/tablesImport.d.ts +57 -0
  86. package/dist/tablesImport.d.ts.map +1 -0
  87. package/dist/tablesImport.js +263 -0
  88. package/dist/turnCache.d.ts +14 -1
  89. package/dist/turnCache.d.ts.map +1 -1
  90. package/dist/turnCache.js +25 -8
  91. package/package.json +6 -3
@@ -0,0 +1,6 @@
1
+ import type { CapabilityPosture } from './capabilityPosture';
2
+ export declare function lookupPosture(capabilityId: string): CapabilityPosture | null;
3
+ export declare function rememberPosture(capabilityId: string, posture: CapabilityPosture): void;
4
+ /** Records the hash from a response. A change clears cached postures. */
5
+ export declare function noteRegistryHash(hash: string | null): void;
6
+ //# sourceMappingURL=registryManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registryManifest.d.ts","sourceRoot":"","sources":["../src/registryManifest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAuD7D,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAE5E;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAItF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAK1D"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lookupPosture = lookupPosture;
4
+ exports.rememberPosture = rememberPosture;
5
+ exports.noteRegistryHash = noteRegistryHash;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_os_1 = require("node:os");
8
+ const node_path_1 = require("node:path");
9
+ function manifestPath(home = (0, node_os_1.homedir)()) {
10
+ return (0, node_path_1.join)(home, '.dreamstate', 'registry-manifest.json');
11
+ }
12
+ function empty() {
13
+ return { registry_hash: null, postures: {} };
14
+ }
15
+ function readManifest() {
16
+ try {
17
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(manifestPath(), 'utf8'));
18
+ const root = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
19
+ if (!root)
20
+ return empty();
21
+ const postures = {};
22
+ const raw = root.postures && typeof root.postures === 'object' && !Array.isArray(root.postures)
23
+ ? root.postures
24
+ : {};
25
+ for (const [id, value] of Object.entries(raw)) {
26
+ if (!value || typeof value !== 'object' || Array.isArray(value))
27
+ continue;
28
+ const posture = value;
29
+ postures[id] = { inlineRead: posture.inlineRead === true, returnsRunId: posture.returnsRunId === true };
30
+ }
31
+ return {
32
+ registry_hash: typeof root.registry_hash === 'string' && root.registry_hash ? root.registry_hash : null,
33
+ postures,
34
+ };
35
+ }
36
+ catch {
37
+ return empty();
38
+ }
39
+ }
40
+ function writeManifest(manifest) {
41
+ try {
42
+ const path = manifestPath();
43
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
44
+ (0, node_fs_1.writeFileSync)(path, `${JSON.stringify(manifest)}\n`, 'utf8');
45
+ }
46
+ catch {
47
+ // Cache only. A request still runs when the home directory is not writable.
48
+ }
49
+ }
50
+ function lookupPosture(capabilityId) {
51
+ return readManifest().postures[capabilityId] ?? null;
52
+ }
53
+ function rememberPosture(capabilityId, posture) {
54
+ const manifest = readManifest();
55
+ manifest.postures[capabilityId] = posture;
56
+ writeManifest(manifest);
57
+ }
58
+ /** Records the hash from a response. A change clears cached postures. */
59
+ function noteRegistryHash(hash) {
60
+ if (!hash)
61
+ return;
62
+ const manifest = readManifest();
63
+ if (manifest.registry_hash === hash)
64
+ return;
65
+ writeManifest({ registry_hash: hash, postures: manifest.registry_hash ? {} : manifest.postures });
66
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Run-door constants. Frozen names are RUN_MODES, QUOTE_HASH_PATTERN and
3
+ * IDEMPOTENCY_KEY_MAX_LENGTH. The source is packages/types/src/agentSurface.ts.
4
+ * The published CLI cannot depend on the unpublished types package, so the
5
+ * values are generated into src/generated/runContract.generated.ts.
6
+ */
7
+ import { IDEMPOTENCY_KEY_MAX_LENGTH, QUOTE_HASH_PATTERN, RUN_MODES } from './generated/runContract.generated';
8
+ export { IDEMPOTENCY_KEY_MAX_LENGTH, QUOTE_HASH_PATTERN, RUN_MODES };
9
+ export type RunMode = (typeof RUN_MODES)[number];
10
+ //# sourceMappingURL=runContract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runContract.d.ts","sourceRoot":"","sources":["../src/runContract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,SAAS,EACV,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC;AACrE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RUN_MODES = exports.QUOTE_HASH_PATTERN = exports.IDEMPOTENCY_KEY_MAX_LENGTH = void 0;
4
+ /**
5
+ * Run-door constants. Frozen names are RUN_MODES, QUOTE_HASH_PATTERN and
6
+ * IDEMPOTENCY_KEY_MAX_LENGTH. The source is packages/types/src/agentSurface.ts.
7
+ * The published CLI cannot depend on the unpublished types package, so the
8
+ * values are generated into src/generated/runContract.generated.ts.
9
+ */
10
+ const runContract_generated_1 = require("./generated/runContract.generated");
11
+ Object.defineProperty(exports, "IDEMPOTENCY_KEY_MAX_LENGTH", { enumerable: true, get: function () { return runContract_generated_1.IDEMPOTENCY_KEY_MAX_LENGTH; } });
12
+ Object.defineProperty(exports, "QUOTE_HASH_PATTERN", { enumerable: true, get: function () { return runContract_generated_1.QUOTE_HASH_PATTERN; } });
13
+ Object.defineProperty(exports, "RUN_MODES", { enumerable: true, get: function () { return runContract_generated_1.RUN_MODES; } });
package/dist/runWait.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { DreamstateClient } from './client';
2
- export declare const RUN_WAIT_TIMEOUT_EXIT_CODE = 8;
3
- export declare function columnRunReturnsRunId(capabilityId: string): boolean;
2
+ /** The wait ran out with the run still active; the latest run envelope is printed. */
3
+ export declare const RUN_WAIT_TIMEOUT_EXIT_CODE: number;
4
4
  export interface RunWaitResult {
5
5
  value: unknown;
6
6
  timedOut: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"runWait.d.ts","sourceRoot":"","sources":["../src/runWait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAOjD,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAY5C,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+BD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
1
+ {"version":3,"file":"runWait.d.ts","sourceRoot":"","sources":["../src/runWait.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAQjD,sFAAsF;AACtF,eAAO,MAAM,0BAA0B,EAAE,MAAyB,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+BD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
package/dist/runWait.js CHANGED
@@ -1,25 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RUN_WAIT_TIMEOUT_EXIT_CODE = void 0;
4
- exports.columnRunReturnsRunId = columnRunReturnsRunId;
5
4
  exports.waitForRun = waitForRun;
5
+ const errors_1 = require("./errors");
6
6
  const TERMINAL_STATUSES = new Set([
7
7
  'succeeded', 'completed', 'completed_with_errors', 'failed', 'cancelled', 'canceled', 'partially_completed',
8
8
  'timed_out', 'rejected', 'awaiting_approval', 'awaiting_input', 'waiting', 'waiting_approval', 'approval_required',
9
9
  ]);
10
- exports.RUN_WAIT_TIMEOUT_EXIT_CODE = 8;
11
- const COLUMN_RUN_HAND_OFF = new Set([
12
- 'tables.columns.run',
13
- 'tables.columns.run_all',
14
- 'tables.columns.run_remaining',
15
- ]);
16
- // timing:async column runs. The tools response is the durable run id. Waiting
17
- // folds every provider row into the command. Progress and final cells are
18
- // runs_get and rows.query. Other mutations still wait so the next command can
19
- // read steps[0].outputs.
20
- function columnRunReturnsRunId(capabilityId) {
21
- return COLUMN_RUN_HAND_OFF.has(capabilityId);
22
- }
10
+ /** The wait ran out with the run still active; the latest run envelope is printed. */
11
+ exports.RUN_WAIT_TIMEOUT_EXIT_CODE = errors_1.CLI_EXIT.TIMEOUT;
23
12
  function runFromEnvelope(value) {
24
13
  if (!value || typeof value !== 'object' || Array.isArray(value))
25
14
  return null;
@@ -0,0 +1,20 @@
1
+ export interface CoercionResult {
2
+ params: Record<string, unknown>;
3
+ /** Property names whose string value was converted. */
4
+ coerced: string[];
5
+ }
6
+ /**
7
+ * Pack verbs intentionally do not carry a second copy of a capability's required
8
+ * fields. `tools get` is source of truth: reject an unknown canonical flag and
9
+ * report fields that schema currently requires after the live contract is read.
10
+ */
11
+ export declare function assertParamsMatchSchema(params: Record<string, unknown>, schema: unknown): void;
12
+ /**
13
+ * Runtime schema coercion for flag-built params. The CLI's pack verbs take their
14
+ * values from flags, and every flag arrives as a string; a numeric property such
15
+ * as max_new_enrollments_per_day was sent as "40" and refused. The type is read
16
+ * from the capability's live input schema (tools get), never from a hand-kept
17
+ * list, so a new numeric field works on the day the catalog adds it.
18
+ */
19
+ export declare function coerceParamsToSchema(params: Record<string, unknown>, schema: unknown): CoercionResult;
20
+ //# sourceMappingURL=schemaCoercion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaCoercion.d.ts","sourceRoot":"","sources":["../src/schemaCoercion.ts"],"names":[],"mappings":"AAoEA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAS9F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,cAAc,CAerG"}
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertParamsMatchSchema = assertParamsMatchSchema;
4
+ exports.coerceParamsToSchema = coerceParamsToSchema;
5
+ function isObject(value) {
6
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+ /** Follows a local `#/$defs/x` or `#/definitions/x` pointer (tools get hoists repeated subschemas there). */
9
+ function resolveRef(node, root, depth = 0) {
10
+ const ref = node.$ref;
11
+ if (typeof ref !== 'string' || !ref.startsWith('#/') || depth > 8)
12
+ return node;
13
+ let current = root;
14
+ for (const part of ref.slice(2).split('/')) {
15
+ current = isObject(current) ? current[decodeURIComponent(part)] : undefined;
16
+ }
17
+ return isObject(current) ? resolveRef(current, root, depth + 1) : node;
18
+ }
19
+ /** Every JSON type a (possibly union) schema accepts. */
20
+ function acceptedTypes(node, root, depth = 0) {
21
+ const types = new Set();
22
+ if (!isObject(node) || depth > 8)
23
+ return types;
24
+ const resolved = resolveRef(node, root);
25
+ const declared = resolved.type;
26
+ for (const type of Array.isArray(declared) ? declared : declared === undefined ? [] : [declared]) {
27
+ if (typeof type === 'string')
28
+ types.add(type);
29
+ }
30
+ for (const key of ['anyOf', 'oneOf']) {
31
+ const branches = resolved[key];
32
+ if (Array.isArray(branches))
33
+ for (const branch of branches)
34
+ acceptedTypes(branch, root, depth + 1).forEach((type) => types.add(type));
35
+ }
36
+ if (Array.isArray(resolved.allOf)) {
37
+ for (const branch of resolved.allOf)
38
+ acceptedTypes(branch, root, depth + 1).forEach((type) => types.add(type));
39
+ }
40
+ if (Array.isArray(resolved.enum) && resolved.enum.some((member) => typeof member === 'string'))
41
+ types.add('string');
42
+ return types;
43
+ }
44
+ const NUMBER_TEXT = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
45
+ /**
46
+ * One flag value, as the shell delivered it (a string), turned into the type the
47
+ * capability's input schema declares. A string-typed property is never touched,
48
+ * so a value the author meant as text (a name that looks like a number) survives.
49
+ */
50
+ function coerceValue(value, types) {
51
+ if (types.has('string') || types.size === 0)
52
+ return value;
53
+ const text = value.trim();
54
+ if ((types.has('integer') || types.has('number')) && NUMBER_TEXT.test(text)) {
55
+ const parsed = Number(text);
56
+ if (Number.isFinite(parsed) && (types.has('number') || Number.isInteger(parsed)))
57
+ return parsed;
58
+ }
59
+ if (types.has('boolean')) {
60
+ if (text === 'true')
61
+ return true;
62
+ if (text === 'false')
63
+ return false;
64
+ }
65
+ if (types.has('array') || types.has('object')) {
66
+ try {
67
+ const parsed = JSON.parse(text);
68
+ if (Array.isArray(parsed) ? types.has('array') : isObject(parsed) && types.has('object'))
69
+ return parsed;
70
+ }
71
+ catch {
72
+ // Not JSON: leave it, and the server's schema refusal names the field.
73
+ }
74
+ }
75
+ if (types.has('null') && text === 'null')
76
+ return null;
77
+ return value;
78
+ }
79
+ /**
80
+ * Pack verbs intentionally do not carry a second copy of a capability's required
81
+ * fields. `tools get` is source of truth: reject an unknown canonical flag and
82
+ * report fields that schema currently requires after the live contract is read.
83
+ */
84
+ function assertParamsMatchSchema(params, schema) {
85
+ if (!isObject(schema) || !isObject(schema.properties))
86
+ return;
87
+ const properties = schema.properties;
88
+ if (Object.keys(properties).length === 0)
89
+ return;
90
+ const unknown = Object.keys(params).filter((key) => !(key in properties));
91
+ if (unknown.length)
92
+ throw new Error(`live schema does not declare parameter(s): ${unknown.join(', ')}`);
93
+ const required = Array.isArray(schema.required) ? schema.required.filter((key) => typeof key === 'string') : [];
94
+ const missing = required.filter((key) => params[key] === undefined);
95
+ if (missing.length)
96
+ throw new Error(`live schema requires parameter(s): ${missing.join(', ')}`);
97
+ }
98
+ /**
99
+ * Runtime schema coercion for flag-built params. The CLI's pack verbs take their
100
+ * values from flags, and every flag arrives as a string; a numeric property such
101
+ * as max_new_enrollments_per_day was sent as "40" and refused. The type is read
102
+ * from the capability's live input schema (tools get), never from a hand-kept
103
+ * list, so a new numeric field works on the day the catalog adds it.
104
+ */
105
+ function coerceParamsToSchema(params, schema) {
106
+ if (!isObject(schema) || !isObject(schema.properties))
107
+ return { params, coerced: [] };
108
+ const out = { ...params };
109
+ const coerced = [];
110
+ for (const [key, value] of Object.entries(params)) {
111
+ if (typeof value !== 'string')
112
+ continue;
113
+ const property = schema.properties[key];
114
+ if (!isObject(property))
115
+ continue;
116
+ const next = coerceValue(value, acceptedTypes(property, schema));
117
+ if (next !== value) {
118
+ out[key] = next;
119
+ coerced.push(key);
120
+ }
121
+ }
122
+ return { params: out, coerced };
123
+ }
@@ -10,6 +10,13 @@ export interface HostedSkill {
10
10
  name: string;
11
11
  content: string;
12
12
  }>;
13
+ /** The server's revision of SKILL.md; part of the content stamp doctor compares. */
14
+ revision_number?: number;
15
+ /** Supporting pages the server lists for this skill, with their revisions. */
16
+ files?: Array<{
17
+ path: string;
18
+ revision_number?: number;
19
+ }>;
13
20
  }
14
21
  export interface SkillInstallOptions {
15
22
  agents: readonly SkillInstallAgent[];
@@ -23,6 +30,15 @@ export interface SkillInstallOptions {
23
30
  export declare function canonicalSkillsDirectory(homeDirectory?: string, project?: boolean, cwd?: string): string;
24
31
  export declare function skillInstallDirectory(agent: SkillInstallAgent, homeDirectory?: string, project?: boolean, cwd?: string): string;
25
32
  export declare function detectInstalledSkillAgents(homeDirectory?: string): Promise<Array<'codex' | 'claude-code' | 'cursor'>>;
33
+ /**
34
+ * What identifies one skill's live content: its text and frontmatter, the server's
35
+ * revision of it, and the supporting pages with theirs. Install stamps this into
36
+ * every file it writes; `skills doctor` recomputes it from the live skills, so a
37
+ * skill that changed on the server is stale however recently it was installed.
38
+ */
39
+ export declare function skillContentHash(skill: HostedSkill): string;
40
+ /** Version of the whole skill set, stamped as `skills=` in every installed file. */
41
+ export declare function skillIndexVersion(skills: readonly HostedSkill[]): string;
26
42
  export declare function renderSkillMarkdown(skill: HostedSkill, provenance: {
27
43
  workspaceId: string;
28
44
  indexVersion: string;
@@ -48,4 +64,28 @@ export declare function inspectSkillInstallers(agents: readonly SkillInstallAgen
48
64
  }>;
49
65
  provenance_age_ms: number | null;
50
66
  }>>;
67
+ export interface SkillDrift {
68
+ /** Installed skill directories found under the canonical skills directory. */
69
+ installed_skill_ids: string[];
70
+ live_skill_ids: string[];
71
+ /** Live skills with no installed copy. */
72
+ missing: string[];
73
+ /** Installed Dreamstate skills the server no longer lists. */
74
+ extra: string[];
75
+ /** Installed skills whose content stamp differs from the live content. */
76
+ stale: Array<{
77
+ id: string;
78
+ installed: string | null;
79
+ live: string;
80
+ }>;
81
+ in_sync: boolean;
82
+ /** The command that brings the install up to date. */
83
+ refresh_command: string;
84
+ }
85
+ /**
86
+ * Compares what is installed with the live skills, by content stamp and not by age.
87
+ * An install an hour old that predates a server-side edit is stale; one from last
88
+ * month that nothing has changed since is current.
89
+ */
90
+ export declare function compareInstalledSkills(live: readonly HostedSkill[], homeDirectory?: string, project?: boolean, cwd?: string): Promise<SkillDrift>;
51
91
  //# sourceMappingURL=skillInstaller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skillInstaller.d.ts","sourceRoot":"","sources":["../src/skillInstaller.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,oBAAoB,+EAAgF,CAAC;AAClH,MAAM,MAAM,iBAAiB,GAAG,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,wBAAgB,wBAAwB,CAAC,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,MAAM,CAEhH;AAiBD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,iBAAiB,EAAE,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,MAAM,CAIvI;AAQD,wBAAsB,0BAA0B,CAAC,aAAa,SAAY,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC,CAAC,CAM9H;AA8BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAczH;AA8BD,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,QAAQ,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAgB1N;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,WAAW,CAO3D;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EAAE,aAAa,SAAY,EAAE,OAAO,UAAQ,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAkC1W"}
1
+ {"version":3,"file":"skillInstaller.d.ts","sourceRoot":"","sources":["../src/skillInstaller.ts"],"names":[],"mappings":"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"}
@@ -32,23 +32,32 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.SKILL_INSTALL_AGENTS = void 0;
37
40
  exports.canonicalSkillsDirectory = canonicalSkillsDirectory;
38
41
  exports.skillInstallDirectory = skillInstallDirectory;
39
42
  exports.detectInstalledSkillAgents = detectInstalledSkillAgents;
43
+ exports.skillContentHash = skillContentHash;
44
+ exports.skillIndexVersion = skillIndexVersion;
40
45
  exports.renderSkillMarkdown = renderSkillMarkdown;
41
46
  exports.installSkills = installSkills;
42
47
  exports.routerSkill = routerSkill;
43
48
  exports.inspectSkillInstallers = inspectSkillInstallers;
49
+ exports.compareInstalledSkills = compareInstalledSkills;
44
50
  const promises_1 = require("node:fs/promises");
45
51
  const node_fs_1 = require("node:fs");
46
52
  const node_crypto_1 = require("node:crypto");
47
53
  const node_os_1 = require("node:os");
48
54
  const node_path_1 = require("node:path");
49
- const index_1 = require("./index");
55
+ const agentGuidance_generated_json_1 = __importDefault(require("./agentGuidance.generated.json"));
56
+ const errors_1 = require("./errors");
57
+ const idempotency_1 = require("./idempotency");
50
58
  exports.SKILL_INSTALL_AGENTS = ['codex', 'claude-code', 'cursor', 'opencode', 'hermes', 'openclaw'];
51
59
  const allowedTools = 'Bash(dreamstate *), Bash(jq *)';
60
+ const PROVENANCE_MARKER = '<!-- dreamstate provenance:';
52
61
  const safeId = /^[a-z0-9][a-z0-9_-]*$/i;
53
62
  function canonicalSkillsDirectory(homeDirectory = (0, node_os_1.homedir)(), project = false, cwd = process.cwd()) {
54
63
  return project ? (0, node_path_1.join)(cwd, '.agents', 'skills') : (0, node_path_1.join)(homeDirectory, '.agents', 'skills');
@@ -103,14 +112,42 @@ async function agentInstalled(agent, homeDirectory, project, cwd) {
103
112
  return false;
104
113
  }
105
114
  }
115
+ /**
116
+ * What identifies one skill's live content: its text and frontmatter, the server's
117
+ * revision of it, and the supporting pages with theirs. Install stamps this into
118
+ * every file it writes; `skills doctor` recomputes it from the live skills, so a
119
+ * skill that changed on the server is stale however recently it was installed.
120
+ */
121
+ function skillContentHash(skill) {
122
+ return (0, node_crypto_1.createHash)('sha256').update((0, idempotency_1.stableJson)({
123
+ id: skill.id,
124
+ description: skill.description,
125
+ body: skill.body,
126
+ frontmatter: skill.frontmatter ?? {},
127
+ revision_number: skill.revision_number ?? null,
128
+ files: [...(skill.files ?? [])].map((file) => ({ path: file.path, revision_number: file.revision_number ?? null })).sort((a, b) => a.path.localeCompare(b.path)),
129
+ })).digest('hex').slice(0, 16);
130
+ }
131
+ /** Version of the whole skill set, stamped as `skills=` in every installed file. */
132
+ function skillIndexVersion(skills) {
133
+ return (0, node_crypto_1.createHash)('sha256')
134
+ .update(JSON.stringify(skills.map((skill) => [skill.id, skillContentHash(skill)])))
135
+ .digest('hex').slice(0, 16);
136
+ }
106
137
  function yamlValue(value) { return JSON.stringify(value); }
107
138
  function skillName(id) {
108
139
  if (!safeId.test(id))
109
- throw new index_1.CliUsageError('Dreamstate API returned an invalid skill id.');
140
+ throw new errors_1.CliUsageError('Dreamstate API returned an invalid skill id.');
110
141
  return id === 'router' ? 'dreamstate' : `dreamstate-${id}`;
111
142
  }
143
+ // The router steps and the paid-run consent rule are not written here. They come from
144
+ // agentGuidance.generated.json, which packages/cli/scripts/build-agent-plugin.mjs generates
145
+ // from the same source as the plugin router and the server's MCP instructions.
146
+ function routerStepLines() {
147
+ return agentGuidance_generated_json_1.default.router_steps.map((step, index) => `${index + 1}. \`${step.cli}\` (MCP \`${step.mcp}\`) ${step.text}`).join('\n');
148
+ }
112
149
  function commandGuide(packs) {
113
- return `## How to run this in Dreamstate\n\nUse \`dreamstate whoami --json\` (MCP \`whoami\`) to verify workspace. Route ambiguous work with \`dreamstate packs route --goal "<goal>" --json\` (MCP \`packs_route\`). Discover actions with \`dreamstate tools search --goal "<goal>" --json\` (MCP \`tools_search\`), inspect exact input with \`dreamstate tools get <capability_id> --json\` (MCP \`tools_get\`), then execute with \`dreamstate tools run <capability_id> --params '{...}' --mode <dry_run|live> --json\` (MCP \`tools_run\`). Check asynchronous work with \`dreamstate runs get <run_id> --json\` (MCP \`runs_get\`).\n\nGoals: ${packs.length ? packs.join(', ') : 'not declared'}.`;
150
+ return `## How to run this in Dreamstate\n\n${routerStepLines()}\n\n${agentGuidance_generated_json_1.default.consent.summary}\n\nGoals: ${packs.length ? packs.join(', ') : 'not declared'}.`;
114
151
  }
115
152
  function packs(frontmatter) {
116
153
  const value = frontmatter.packs;
@@ -129,20 +166,41 @@ function renderSkillMarkdown(skill, provenance) {
129
166
  const lines = Object.entries(frontmatter).flatMap(([key, value]) => Array.isArray(value)
130
167
  ? [`${key}: [${value.map(yamlValue).join(', ')}]`]
131
168
  : [`${key}: ${yamlValue(value)}`]);
132
- return `---\n${lines.join('\n')}\n---\n\n${commandGuide(packs(frontmatter))}\n\n${skill.body.trim()}\n\n<!-- dreamstate provenance: workspace=${provenance.workspaceId} issued=${new Date().toISOString()} skills=${provenance.indexVersion} -->\n`;
169
+ return `---\n${lines.join('\n')}\n---\n\n${commandGuide(packs(frontmatter))}\n\n${skill.body.trim()}\n\n<!-- dreamstate provenance: workspace=${provenance.workspaceId} issued=${new Date().toISOString()} skills=${provenance.indexVersion} content=${skillContentHash(skill)} -->\n`;
170
+ }
171
+ /**
172
+ * A supporting page's path under its skill directory, or null when it is not a plain
173
+ * relative path of safe segments. A remote name becomes a path, so traversal and
174
+ * absolute paths are refused here rather than trusted.
175
+ */
176
+ function supportingFilePath(directory, name) {
177
+ if (!name || name.startsWith('/') || name.includes('\\') || name.includes('\0'))
178
+ return null;
179
+ const segments = (0, node_path_1.normalize)(name).split(node_path_1.sep);
180
+ if (segments.some((segment) => !segment || segment === '..' || segment === '.' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(segment)))
181
+ return null;
182
+ if (segments.join('/') === 'SKILL.md')
183
+ return null;
184
+ return (0, node_path_1.join)(directory, ...segments);
133
185
  }
134
186
  function hash(value) { return (0, node_crypto_1.createHash)('sha256').update(value).digest('hex'); }
135
187
  async function writeHostedSkill(root, skill, provenance) {
136
188
  const name = skillName(skill.id);
137
189
  const directory = (0, node_path_1.join)(root, name);
138
190
  const markdown = renderSkillMarkdown(skill, provenance);
191
+ // A page removed upstream must not linger: clear a directory this CLI wrote before rewriting it.
192
+ if ((await (0, promises_1.readFile)((0, node_path_1.join)(directory, 'SKILL.md'), 'utf8').catch(() => '')).includes(PROVENANCE_MARKER)) {
193
+ await (0, promises_1.rm)(directory, { recursive: true, force: true });
194
+ }
139
195
  await (0, promises_1.mkdir)(directory, { recursive: true, mode: 0o700 });
140
196
  const file = (0, node_path_1.join)(directory, 'SKILL.md');
141
197
  await (0, promises_1.writeFile)(file, markdown, { encoding: 'utf8', mode: 0o600 });
142
198
  for (const document of skill.documents ?? []) {
143
- if (!safeId.test(document.name.replace(/\.[a-z0-9]+$/i, '')))
199
+ const target = supportingFilePath(directory, document.name);
200
+ if (!target)
144
201
  continue;
145
- await (0, promises_1.writeFile)((0, node_path_1.join)(directory, document.name), document.content, { encoding: 'utf8', mode: 0o600 });
202
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(target), { recursive: true, mode: 0o700 });
203
+ await (0, promises_1.writeFile)(target, document.content, { encoding: 'utf8', mode: 0o600 });
146
204
  }
147
205
  return { file, name, computedHash: hash(markdown) };
148
206
  }
@@ -151,8 +209,9 @@ async function linkAgentSkill(agentRoot, canonicalDirectory, name, copy) {
151
209
  await (0, promises_1.mkdir)(agentRoot, { recursive: true, mode: 0o700 });
152
210
  await (0, promises_1.rm)(targetDirectory, { recursive: true, force: true });
153
211
  if (copy) {
154
- await (0, promises_1.mkdir)(targetDirectory, { recursive: true, mode: 0o700 });
155
- await (0, promises_1.writeFile)((0, node_path_1.join)(targetDirectory, 'SKILL.md'), await (0, promises_1.readFile)((0, node_path_1.join)(canonicalDirectory, name, 'SKILL.md')), { encoding: 'utf8', mode: 0o600 });
212
+ // The whole skill directory, not only SKILL.md: the supporting pages the skill body
213
+ // links to must travel with it, or a copied skill points at files that are not there.
214
+ await (0, promises_1.cp)((0, node_path_1.join)(canonicalDirectory, name), targetDirectory, { recursive: true });
156
215
  }
157
216
  else {
158
217
  await (0, promises_1.symlink)((0, node_path_1.relative)(agentRoot, (0, node_path_1.join)(canonicalDirectory, name)), targetDirectory, 'dir');
@@ -185,7 +244,7 @@ function routerSkill(packsList) {
185
244
  const goals = records.map((item) => item && typeof item === 'object' ? item : {})
186
245
  .filter((item) => typeof item.id === 'string');
187
246
  const lines = goals.map((pack) => `- ${pack.id}: ${pack.owns ?? 'Dreamstate capability pack.'}${pack.not_for ? ` ${pack.not_for}` : ''}`).join('\n');
188
- return { id: 'router', name: 'Dreamstate', description: 'Route work to Dreamstate capability skills.', body: `# Dreamstate router\n\nPrefer narrower \`dreamstate-*\` skill when one fits. Otherwise route with \`dreamstate packs route --goal "<goal>" --json\` (MCP \`packs_route\`), search tools scoped by the returned pack with \`dreamstate tools search --goal "<goal>" --toolset <pack_id> --json\` (MCP \`tools_search\`), fetch exact schema with \`dreamstate tools get <capability_id> --json\` (MCP \`tools_get\`), then run it with \`dreamstate tools run <capability_id> --params '{...}' --json\` (MCP \`tools_run\`).\n\n## Goals\n\n${lines || 'No packs returned by GET /packs.'}`, frontmatter: { packs: goals.map((pack) => pack.id) } };
247
+ return { id: 'router', name: 'Dreamstate', description: 'Route work to Dreamstate capability skills.', body: `# Dreamstate router\n\nPrefer a narrower \`dreamstate-*\` skill when one fits. Otherwise:\n\n${routerStepLines()}\n\n## Paid and outward-facing runs\n\n${agentGuidance_generated_json_1.default.consent.paid_run}\n\n${agentGuidance_generated_json_1.default.consent.human_approval}\n\n## Goals\n\n${lines || 'No packs returned by GET /packs.'}`, frontmatter: { packs: goals.map((pack) => pack.id) } };
189
248
  }
190
249
  async function inspectSkillInstallers(agents, homeDirectory = (0, node_os_1.homedir)(), project = false, cwd = process.cwd()) {
191
250
  const canonical = canonicalSkillsDirectory(homeDirectory, project, cwd);
@@ -234,3 +293,43 @@ async function inspectSkillInstallers(agents, homeDirectory = (0, node_os_1.home
234
293
  return { agent, directory, writable, canonical_directory: canonical, symlinks, provenance_age_ms };
235
294
  }));
236
295
  }
296
+ const PROVENANCE_PATTERN = /<!-- dreamstate provenance: workspace=[^\s]+ issued=[^\s]+ skills=[^\s]+(?: content=([0-9a-f]+))? -->/;
297
+ /**
298
+ * Compares what is installed with the live skills, by content stamp and not by age.
299
+ * An install an hour old that predates a server-side edit is stale; one from last
300
+ * month that nothing has changed since is current.
301
+ */
302
+ async function compareInstalledSkills(live, homeDirectory = (0, node_os_1.homedir)(), project = false, cwd = process.cwd()) {
303
+ const canonical = canonicalSkillsDirectory(homeDirectory, project, cwd);
304
+ let names = [];
305
+ try {
306
+ names = (await (0, promises_1.readdir)(canonical)).filter((name) => name === 'dreamstate' || name.startsWith('dreamstate-'));
307
+ }
308
+ catch { /* nothing installed */ }
309
+ const installedStamps = new Map();
310
+ for (const name of names) {
311
+ const id = name === 'dreamstate' ? 'router' : name.slice('dreamstate-'.length);
312
+ try {
313
+ const markdown = await (0, promises_1.readFile)((0, node_path_1.join)(canonical, name, 'SKILL.md'), 'utf8');
314
+ installedStamps.set(id, PROVENANCE_PATTERN.exec(markdown)?.[1] ?? null);
315
+ }
316
+ catch {
317
+ installedStamps.set(id, null);
318
+ }
319
+ }
320
+ const liveIds = live.map((skill) => skill.id);
321
+ const missing = liveIds.filter((id) => !installedStamps.has(id));
322
+ const extra = [...installedStamps.keys()].filter((id) => !liveIds.includes(id)).sort();
323
+ const stale = live
324
+ .filter((skill) => installedStamps.has(skill.id) && installedStamps.get(skill.id) !== skillContentHash(skill))
325
+ .map((skill) => ({ id: skill.id, installed: installedStamps.get(skill.id) ?? null, live: skillContentHash(skill) }));
326
+ return {
327
+ installed_skill_ids: [...installedStamps.keys()].sort(),
328
+ live_skill_ids: liveIds,
329
+ missing,
330
+ extra,
331
+ stale,
332
+ in_sync: missing.length === 0 && extra.length === 0 && stale.length === 0,
333
+ refresh_command: `dreamstate skills install${project ? ' --project' : ''}`,
334
+ };
335
+ }
@@ -8,5 +8,19 @@ export interface LocalTableFilePreview {
8
8
  writes: false;
9
9
  }
10
10
  export declare function inferLocalTableFileKind(filePath: string, format?: string): LocalTableFileKind;
11
+ export declare function parseCsv(text: string): {
12
+ rows: string[][];
13
+ endedClean: boolean;
14
+ unterminatedQuote: boolean;
15
+ };
16
+ /**
17
+ * The first worksheet of an xlsx file as rows of cell text. `rowLimit` stops at
18
+ * that many rows (the preview needs a header and a sample); null reads them all
19
+ * (an import needs every row).
20
+ */
21
+ export declare function readXlsxRows(buffer: Buffer, rowLimit: number | null): {
22
+ rows: string[][];
23
+ totalRows: number;
24
+ };
11
25
  export declare function previewLocalTableFile(filePath: string, format?: string): Promise<LocalTableFilePreview>;
12
26
  //# sourceMappingURL=tableFilePreview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tableFilePreview.d.ts","sourceRoot":"","sources":["../src/tableFilePreview.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEnE,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;CACf;AAgBD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAK7F;AAoOD,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgB7G"}
1
+ {"version":3,"file":"tableFilePreview.d.ts","sourceRoot":"","sources":["../src/tableFilePreview.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEnE,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;CACf;AAgBD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAK7F;AAyBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAE,CAwC5G;AA0HD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAsC7G;AAQD,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgB7G"}