@trydreamstate/cli 1.3.1 → 1.3.3

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 (76) hide show
  1. package/README.md +29 -16
  2. package/agent-plugin/.claude-plugin/plugin.json +1 -1
  3. package/agent-plugin/.codex-plugin/plugin.json +4 -1
  4. package/agent-plugin/assets/logo.png +0 -0
  5. package/agent-plugin/hooks/approve-readonly.sh +15 -0
  6. package/agent-plugin/hooks/hooks.json +17 -0
  7. package/agent-plugin/skills/crm/SKILL.md +2 -2
  8. package/agent-plugin/skills/diagnostics/SKILL.md +1 -1
  9. package/agent-plugin/skills/dreamstate/SKILL.md +13 -5
  10. package/agent-plugin/skills/first-win/SKILL.md +2 -2
  11. package/agent-plugin/skills/inbox-replies/SKILL.md +1 -1
  12. package/agent-plugin/skills/knowledge/SKILL.md +1 -1
  13. package/agent-plugin/skills/linkedin-posts/SKILL.md +1 -1
  14. package/agent-plugin/skills/linkedin-warm-signals/SKILL.md +2 -2
  15. package/agent-plugin/skills/list-building/SKILL.md +4 -4
  16. package/agent-plugin/skills/outreach/SKILL.md +1 -1
  17. package/agent-plugin/skills/outreach/steps-and-timing.md +1 -1
  18. package/agent-plugin/skills/plays/SKILL.md +2 -2
  19. package/agent-plugin/skills/reddit-posts/SKILL.md +1 -1
  20. package/agent-plugin/skills/skill-authoring/SKILL.md +1 -1
  21. package/agent-plugin/skills/social/SKILL.md +2 -2
  22. package/agent-plugin/skills/visibility/SKILL.md +2 -2
  23. package/agent-plugin/skills/workflows/SKILL.md +3 -3
  24. package/agent-plugin/skills/worksheet-tidy/SKILL.md +1 -1
  25. package/agent-plugin/skills/writing/SKILL.md +1 -1
  26. package/agent-plugin/skills/x-posts/SKILL.md +1 -1
  27. package/dist/agentGuidance.generated.json +35 -0
  28. package/dist/client.d.ts +31 -2
  29. package/dist/client.d.ts.map +1 -1
  30. package/dist/client.js +115 -33
  31. package/dist/commandTable.d.ts +26 -0
  32. package/dist/commandTable.d.ts.map +1 -0
  33. package/dist/commandTable.js +54 -0
  34. package/dist/commands/sequences.d.ts +1 -1
  35. package/dist/commands/sequences.d.ts.map +1 -1
  36. package/dist/commands/sequences.js +37 -36
  37. package/dist/commands/shared.d.ts +4 -26
  38. package/dist/commands/shared.d.ts.map +1 -1
  39. package/dist/commands/shared.js +129 -127
  40. package/dist/commands/workflows.d.ts +1 -1
  41. package/dist/commands/workflows.d.ts.map +1 -1
  42. package/dist/commands/workflows.js +35 -41
  43. package/dist/errors.d.ts +78 -0
  44. package/dist/errors.d.ts.map +1 -0
  45. package/dist/errors.js +117 -0
  46. package/dist/gatedRun.d.ts +81 -0
  47. package/dist/gatedRun.d.ts.map +1 -0
  48. package/dist/gatedRun.js +370 -0
  49. package/dist/idempotency.d.ts +38 -0
  50. package/dist/idempotency.d.ts.map +1 -0
  51. package/dist/idempotency.js +106 -0
  52. package/dist/index.d.ts +21 -35
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +372 -366
  55. package/dist/runWait.d.ts +2 -1
  56. package/dist/runWait.d.ts.map +1 -1
  57. package/dist/runWait.js +3 -1
  58. package/dist/schemaCoercion.d.ts +20 -0
  59. package/dist/schemaCoercion.d.ts.map +1 -0
  60. package/dist/schemaCoercion.js +123 -0
  61. package/dist/skillInstaller.d.ts +40 -0
  62. package/dist/skillInstaller.d.ts.map +1 -1
  63. package/dist/skillInstaller.js +108 -9
  64. package/dist/tableFilePreview.d.ts +14 -0
  65. package/dist/tableFilePreview.d.ts.map +1 -1
  66. package/dist/tableFilePreview.js +19 -8
  67. package/dist/tableFileReader.d.ts +26 -0
  68. package/dist/tableFileReader.d.ts.map +1 -0
  69. package/dist/tableFileReader.js +108 -0
  70. package/dist/tablesImport.d.ts +57 -0
  71. package/dist/tablesImport.d.ts.map +1 -0
  72. package/dist/tablesImport.js +263 -0
  73. package/dist/turnCache.d.ts +14 -1
  74. package/dist/turnCache.d.ts.map +1 -1
  75. package/dist/turnCache.js +25 -8
  76. package/package.json +3 -2
package/dist/runWait.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { DreamstateClient } from './client';
2
- export declare const RUN_WAIT_TIMEOUT_EXIT_CODE = 8;
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;
3
4
  export declare function columnRunReturnsRunId(capabilityId: string): boolean;
4
5
  export interface RunWaitResult {
5
6
  value: unknown;
@@ -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;AAYnE,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"}
package/dist/runWait.js CHANGED
@@ -3,11 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RUN_WAIT_TIMEOUT_EXIT_CODE = void 0;
4
4
  exports.columnRunReturnsRunId = columnRunReturnsRunId;
5
5
  exports.waitForRun = waitForRun;
6
+ const errors_1 = require("./errors");
6
7
  const TERMINAL_STATUSES = new Set([
7
8
  'succeeded', 'completed', 'completed_with_errors', 'failed', 'cancelled', 'canceled', 'partially_completed',
8
9
  'timed_out', 'rejected', 'awaiting_approval', 'awaiting_input', 'waiting', 'waiting_approval', 'approval_required',
9
10
  ]);
10
- exports.RUN_WAIT_TIMEOUT_EXIT_CODE = 8;
11
+ /** The wait ran out with the run still active; the latest run envelope is printed. */
12
+ exports.RUN_WAIT_TIMEOUT_EXIT_CODE = errors_1.CLI_EXIT.TIMEOUT;
11
13
  const COLUMN_RUN_HAND_OFF = new Set([
12
14
  'tables.columns.run',
13
15
  'tables.columns.run_all',
@@ -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"}
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.inferLocalTableFileKind = inferLocalTableFileKind;
4
+ exports.parseCsv = parseCsv;
5
+ exports.readXlsxRows = readXlsxRows;
4
6
  exports.previewLocalTableFile = previewLocalTableFile;
5
7
  const promises_1 = require("node:fs/promises");
6
8
  const node_zlib_1 = require("node:zlib");
@@ -9,7 +11,7 @@ const TEXT_PREVIEW_BYTES = 1_000_000;
9
11
  const XLSX_PREVIEW_BYTES = 20_000_000;
10
12
  const MAPPABLE_FIELDS = [
11
13
  'external_id', 'name', 'first_name', 'last_name', 'company',
12
- 'title', 'headline', 'email', 'linkedin_url', 'profile_url', 'avatar_url',
14
+ 'title', 'headline', 'email', 'linkedin_url', 'profile_url', 'domain', 'avatar_url',
13
15
  ];
14
16
  function normalizeHeaderKey(value) {
15
17
  return value.trim().toLowerCase().replace(/[\s_-]+/g, '');
@@ -101,7 +103,7 @@ function parseCsv(text) {
101
103
  if (!(row.length === 1 && row[0] === ''))
102
104
  rows.push(row);
103
105
  }
104
- return { rows, endedClean };
106
+ return { rows, endedClean, unterminatedQuote: quoted };
105
107
  }
106
108
  function previewDelimited(kind, text, clipped) {
107
109
  const parsed = parseCsv(text);
@@ -234,13 +236,18 @@ function sharedStringTable(xml) {
234
236
  return [...xml.matchAll(/<(?:[\w-]+:)?si\b[^>]*>([\s\S]*?)<\/(?:[\w-]+:)?si>/g)]
235
237
  .map((match) => xmlTexts(match[1] ?? ''));
236
238
  }
237
- function previewXlsx(buffer) {
239
+ /**
240
+ * The first worksheet of an xlsx file as rows of cell text. `rowLimit` stops at
241
+ * that many rows (the preview needs a header and a sample); null reads them all
242
+ * (an import needs every row).
243
+ */
244
+ function readXlsxRows(buffer, rowLimit) {
238
245
  const entries = unzipEntries(buffer);
239
246
  const sheetName = [...entries.keys()]
240
247
  .filter((name) => /xl\/worksheets\/sheet\d+\.xml$/.test(name))
241
248
  .sort()[0];
242
249
  if (!sheetName)
243
- throw new Error('xlsx preview could not find a worksheet');
250
+ throw new Error('xlsx file has no worksheet');
244
251
  const sheet = entries.get(sheetName).toString('utf8');
245
252
  const sharedName = [...entries.keys()].find((name) => name.endsWith('xl/sharedStrings.xml'));
246
253
  const shared = sharedName ? sharedStringTable(entries.get(sharedName).toString('utf8')) : [];
@@ -248,7 +255,7 @@ function previewXlsx(buffer) {
248
255
  for (const rowMatch of sheet.matchAll(/<(?:[\w-]+:)?row\b([^>]*)>([\s\S]*?)<\/(?:[\w-]+:)?row>/g)) {
249
256
  const rowRef = /\br="(\d+)"/.exec(rowMatch[1] ?? '');
250
257
  const rowNumber = rowRef ? Number(rowRef[1]) : grid.size + 1;
251
- if (rowNumber > SAMPLE_ROWS + 1)
258
+ if (rowLimit !== null && rowNumber > rowLimit)
252
259
  continue;
253
260
  const cells = new Map();
254
261
  for (const cellMatch of (rowMatch[2] ?? '').matchAll(/<(?:[\w-]+:)?c\b([^>]*)(?:\/>|>([\s\S]*?)<\/(?:[\w-]+:)?c>)/g)) {
@@ -277,9 +284,13 @@ function previewXlsx(buffer) {
277
284
  throw new Error('File has no header row');
278
285
  const width = Math.max(...[...grid.values()].map((cells) => Math.max(-1, ...cells.keys()))) + 1;
279
286
  const toRow = (cells) => Array.from({ length: width }, (_, index) => cells?.get(index) ?? '');
280
- const [headerNumber, ...dataNumbers] = rowNumbers;
281
- const rowCount = [...sheet.matchAll(/<(?:[\w-]+:)?row\b/g)].length;
282
- return finish('xlsx', toRow(grid.get(headerNumber)), dataNumbers.map((number) => toRow(grid.get(number))), rowCount > SAMPLE_ROWS + 1);
287
+ const totalRows = [...sheet.matchAll(/<(?:[\w-]+:)?row\b/g)].length;
288
+ return { rows: rowNumbers.map((number) => toRow(grid.get(number))), totalRows };
289
+ }
290
+ function previewXlsx(buffer) {
291
+ const { rows, totalRows } = readXlsxRows(buffer, SAMPLE_ROWS + 1);
292
+ const [header, ...dataRows] = rows;
293
+ return finish('xlsx', header ?? [], dataRows, totalRows > SAMPLE_ROWS + 1);
283
294
  }
284
295
  async function previewLocalTableFile(filePath, format) {
285
296
  const kind = inferLocalTableFileKind(filePath, format);
@@ -0,0 +1,26 @@
1
+ import { type LocalTableFileKind } from './tableFilePreview';
2
+ /** One staged row, the wire shape of tables.table_sources.csv_upload_stage.rows. */
3
+ export interface StagedRow {
4
+ source_row_number: number;
5
+ data: Record<string, string>;
6
+ }
7
+ export interface LocalTableFile {
8
+ file_kind: LocalTableFileKind;
9
+ headers: string[];
10
+ rows: StagedRow[];
11
+ }
12
+ /** Largest file an import reads into memory. The server caps one upload at 200,000 rows. */
13
+ export declare const MAX_IMPORT_FILE_BYTES = 250000000;
14
+ /**
15
+ * Duplicate CSV labels cannot be object keys twice, so they get private
16
+ * staging keys; the column definitions keep the labels. Same rule as
17
+ * csvStagingHeaderKeys in packages/types (the server's own parser), pinned by
18
+ * tableFileReader.test.ts against that function.
19
+ */
20
+ export declare function stagingHeaderKeys(headers: readonly string[]): string[];
21
+ /** Every row of a local CSV, JSON, JSONL or XLSX file, shaped for csv_upload_stage. */
22
+ export declare function readLocalTableFile(filePath: string, format?: string): Promise<{
23
+ file: LocalTableFile;
24
+ bytes: Buffer;
25
+ }>;
26
+ //# sourceMappingURL=tableFileReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableFileReader.d.ts","sourceRoot":"","sources":["../src/tableFileReader.ts"],"names":[],"mappings":"AACA,OAAO,EAAmD,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE9G,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,YAAc,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAWtE;AAyCD,uFAAuF;AACvF,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB5H"}