@flytedesk/app-kit 5.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +184 -41
  2. package/dist/auth/client/authSession.d.ts +168 -0
  3. package/dist/auth/client/authSession.js +581 -0
  4. package/dist/auth/client/authSession.js.map +1 -0
  5. package/dist/auth/client/browserEnvironment.d.ts +22 -0
  6. package/dist/auth/client/browserEnvironment.js +87 -0
  7. package/dist/auth/client/browserEnvironment.js.map +1 -0
  8. package/dist/auth/client/httpClient.d.ts +22 -25
  9. package/dist/auth/client/httpClient.js +49 -51
  10. package/dist/auth/client/httpClient.js.map +1 -1
  11. package/dist/auth/client/index.d.ts +15 -8
  12. package/dist/auth/client/index.js +13 -7
  13. package/dist/auth/client/index.js.map +1 -1
  14. package/dist/auth/client/testing.d.ts +6 -0
  15. package/dist/auth/client/testing.js +20 -0
  16. package/dist/auth/client/testing.js.map +1 -0
  17. package/dist/auth/client/useAuthSession.d.ts +54 -91
  18. package/dist/auth/client/useAuthSession.js +65 -252
  19. package/dist/auth/client/useAuthSession.js.map +1 -1
  20. package/dist/auth/index.d.ts +3 -4
  21. package/dist/auth/index.js +1 -2
  22. package/dist/auth/index.js.map +1 -1
  23. package/dist/auth/oidc-client.d.ts +18 -4
  24. package/dist/auth/oidc-client.js +18 -1
  25. package/dist/auth/oidc-client.js.map +1 -1
  26. package/dist/auth/plugin.js +250 -211
  27. package/dist/auth/plugin.js.map +1 -1
  28. package/dist/auth/returnTarget.d.ts +30 -0
  29. package/dist/auth/returnTarget.js +48 -0
  30. package/dist/auth/returnTarget.js.map +1 -0
  31. package/dist/auth/shared-types.d.ts +18 -41
  32. package/dist/auth/shared-types.js +18 -45
  33. package/dist/auth/shared-types.js.map +1 -1
  34. package/dist/auth/testing/fake-idp.d.ts +38 -1
  35. package/dist/auth/testing/fake-idp.js +166 -27
  36. package/dist/auth/testing/fake-idp.js.map +1 -1
  37. package/dist/auth/types.d.ts +26 -29
  38. package/dist/bigquery/errors.d.ts +19 -0
  39. package/dist/bigquery/errors.js +41 -0
  40. package/dist/bigquery/errors.js.map +1 -1
  41. package/dist/bigquery/index.d.ts +2 -2
  42. package/dist/bigquery/index.js +1 -1
  43. package/dist/bigquery/index.js.map +1 -1
  44. package/dist/bigquery/query.js +14 -1
  45. package/dist/bigquery/query.js.map +1 -1
  46. package/dist/bigquery/schema.d.ts +5 -0
  47. package/dist/bigquery/schema.js +35 -0
  48. package/dist/bigquery/schema.js.map +1 -0
  49. package/dist/bigquery/types.d.ts +47 -2
  50. package/dist/chat/index.d.ts +52 -21
  51. package/dist/chat/index.js +50 -20
  52. package/dist/chat/index.js.map +1 -1
  53. package/dist/chat/mcp/entry.d.ts +37 -0
  54. package/dist/chat/mcp/entry.js +135 -0
  55. package/dist/chat/mcp/entry.js.map +1 -0
  56. package/dist/cli/is-running-as-main.d.ts +1 -0
  57. package/dist/cli/is-running-as-main.js +54 -0
  58. package/dist/cli/is-running-as-main.js.map +1 -0
  59. package/dist/cli/sync-engine.d.ts +1 -32
  60. package/dist/cli/sync-engine.js +7 -45
  61. package/dist/cli/sync-engine.js.map +1 -1
  62. package/dist/flags/evaluate.d.ts +43 -0
  63. package/dist/flags/evaluate.js +64 -0
  64. package/dist/flags/evaluate.js.map +1 -0
  65. package/dist/flags/index.d.ts +22 -6
  66. package/dist/flags/index.js +21 -6
  67. package/dist/flags/index.js.map +1 -1
  68. package/dist/flags/plugin.js +73 -7
  69. package/dist/flags/plugin.js.map +1 -1
  70. package/dist/flags/types.d.ts +25 -0
  71. package/package.json +11 -4
@@ -4,8 +4,9 @@
4
4
  * (AK-11). Inventory-specific SQL stays in media-planner; this module only
5
5
  * ever takes SQL + params from the caller and runs it.
6
6
  */
7
- import { BadQueryError, classifyBigQueryError } from "./errors.js";
7
+ import { BadQueryError, classifyBigQueryError, isJobTimeoutError, JobTimeoutError } from "./errors.js";
8
8
  import { DryRunBudgetExceededError } from "./errors.js";
9
+ import { normalizeResultSchema } from "./schema.js";
9
10
  /** Runs one page of a parameterised, read-only query. Always bind
10
11
  * user-supplied values through `options.params`/`options.types` — never
11
12
  * string-interpolate them into `options.sql`.
@@ -34,9 +35,13 @@ export async function runQuery(bq, options) {
34
35
  maximumBytesBilled: options.maximumBytesBilled !== undefined
35
36
  ? String(options.maximumBytesBilled)
36
37
  : undefined,
38
+ jobTimeoutMs: options.jobTimeoutMs,
37
39
  });
38
40
  }
39
41
  catch (err) {
42
+ if (options.jobTimeoutMs !== undefined && isJobTimeoutError(err)) {
43
+ throw new JobTimeoutError(options.jobTimeoutMs, err);
44
+ }
40
45
  throw classifyBigQueryError(err);
41
46
  }
42
47
  }
@@ -52,6 +57,9 @@ export async function runQuery(bq, options) {
52
57
  };
53
58
  }
54
59
  catch (err) {
60
+ if (options.jobTimeoutMs !== undefined && isJobTimeoutError(err)) {
61
+ throw new JobTimeoutError(options.jobTimeoutMs, err);
62
+ }
55
63
  throw classifyBigQueryError(err);
56
64
  }
57
65
  }
@@ -87,9 +95,13 @@ export async function estimateQueryBytes(bq, options) {
87
95
  types: options.types,
88
96
  location: options.location,
89
97
  dryRun: true,
98
+ jobTimeoutMs: options.jobTimeoutMs,
90
99
  });
91
100
  }
92
101
  catch (err) {
102
+ if (options.jobTimeoutMs !== undefined && isJobTimeoutError(err)) {
103
+ throw new JobTimeoutError(options.jobTimeoutMs, err);
104
+ }
93
105
  throw classifyBigQueryError(err);
94
106
  }
95
107
  const stats = metadata.statistics?.query;
@@ -98,6 +110,7 @@ export async function estimateQueryBytes(bq, options) {
98
110
  cacheHit: stats?.cacheHit ?? false,
99
111
  statementType: stats?.statementType,
100
112
  referencedTables: stats?.referencedTables,
113
+ schema: normalizeResultSchema(stats?.schema),
101
114
  };
102
115
  if (options.maxBytesBilled !== undefined &&
103
116
  estimate.totalBytesProcessed > options.maxBytesBilled) {
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/bigquery/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/bigquery/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { BigQueryRawSchema, BigQueryResultSchemaField } from "./types.js";
2
+ /** `undefined` when BigQuery sent no schema at all; `[]` when it sent one with
3
+ * no fields. Throws `MalformedResultSchemaError` for a field lacking a
4
+ * non-empty `name` or `type`, at any nesting depth. */
5
+ export declare function normalizeResultSchema(schema: BigQueryRawSchema | undefined): BigQueryResultSchemaField[] | undefined;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Parses BigQuery's raw dry-run result schema (every property optional, as the
3
+ * real SDK types it) into the normalised `BigQueryResultSchemaField[]` callers
4
+ * get. Parse at the boundary: a field missing `name` or `type` is a typed error
5
+ * here rather than a silent `undefined` downstream (AK-31).
6
+ */
7
+ import { MalformedResultSchemaError } from "./errors.js";
8
+ /** `undefined` when BigQuery sent no schema at all; `[]` when it sent one with
9
+ * no fields. Throws `MalformedResultSchemaError` for a field lacking a
10
+ * non-empty `name` or `type`, at any nesting depth. */
11
+ export function normalizeResultSchema(schema) {
12
+ if (schema === undefined)
13
+ return undefined;
14
+ return normalizeFields(schema.fields ?? [], "");
15
+ }
16
+ function normalizeFields(fields, parentPath) {
17
+ return fields.map((field, index) => normalizeField(field, index, parentPath));
18
+ }
19
+ function normalizeField(field, index, parentPath) {
20
+ const { name, type } = field;
21
+ if (!name) {
22
+ throw new MalformedResultSchemaError(`${parentPath}fields[${index}]`, "name");
23
+ }
24
+ const path = `${parentPath}${name}`;
25
+ if (!type) {
26
+ throw new MalformedResultSchemaError(path, "type");
27
+ }
28
+ const normalized = { name, type };
29
+ if (field.mode !== undefined)
30
+ normalized.mode = field.mode;
31
+ if (field.fields !== undefined)
32
+ normalized.fields = normalizeFields(field.fields, `${path}.`);
33
+ return normalized;
34
+ }
35
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/bigquery/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAOzD;;wDAEwD;AACxD,MAAM,UAAU,qBAAqB,CACnC,MAAqC;IAErC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,eAAe,CACtB,MAAgC,EAChC,UAAkB;IAElB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,cAAc,CACrB,KAA6B,EAC7B,KAAa,EACb,UAAkB;IAElB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,0BAA0B,CAAC,GAAG,UAAU,UAAU,KAAK,GAAG,EAAE,MAAM,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,UAAU,GAAG,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,0BAA0B,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,UAAU,GAA8B,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;QAAE,UAAU,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,UAAU,CAAC,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;IAC9F,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -32,6 +32,20 @@ export interface BigQueryTableReference {
32
32
  datasetId?: string;
33
33
  tableId?: string;
34
34
  }
35
+ /** A result-schema field exactly as BigQuery's job statistics report it: the
36
+ * real SDK's `ITableFieldSchema`, where every property is optional. Only the
37
+ * raw boundary speaks this shape; callers get `BigQueryResultSchemaField`
38
+ * (name and type guaranteed) from `estimateQueryBytes`. */
39
+ export interface BigQueryRawSchemaField {
40
+ name?: string;
41
+ type?: string;
42
+ mode?: string;
43
+ fields?: BigQueryRawSchemaField[];
44
+ }
45
+ /** `IJobStatistics2.schema`, an `ITableSchema`. */
46
+ export interface BigQueryRawSchema {
47
+ fields?: BigQueryRawSchemaField[];
48
+ }
35
49
  export interface BigQueryJobStatistics {
36
50
  query?: {
37
51
  totalBytesProcessed?: string | number;
@@ -42,6 +56,9 @@ export interface BigQueryJobStatistics {
42
56
  statementType?: string;
43
57
  /** Every table the query touches — `IJobStatistics2.referencedTables`. */
44
58
  referencedTables?: BigQueryTableReference[];
59
+ /** The result schema BigQuery computed for the (dry-run) query —
60
+ * `IJobStatistics2.schema`, an `ITableSchema` (`{ fields }`). */
61
+ schema?: BigQueryRawSchema;
45
62
  };
46
63
  }
47
64
  export interface BigQueryJobMetadata {
@@ -92,6 +109,10 @@ export interface BigQueryCreateQueryJobOptions {
92
109
  * than run. Distinct from `EstimateQueryBytesOptions.maxBytesBilled`
93
110
  * (a pre-flight dry-run check this module enforces client-side). */
94
111
  maximumBytesBilled?: string;
112
+ /** BigQuery's own `configuration.jobTimeoutMs` — the job fails with
113
+ * `stopped` state once it runs longer than this, wall-clock, from job
114
+ * creation. The SDK converts this number to the wire's decimal string. */
115
+ jobTimeoutMs?: number;
95
116
  }
96
117
  export interface BigQueryExtractOptions {
97
118
  /** `@google-cloud/bigquery`'s own extract-job vocabulary — note this is
@@ -111,14 +132,25 @@ export interface GcsFileLike {
111
132
  };
112
133
  }
113
134
  /** One BigQuery table-schema field, as `createWriteStream`'s `schema` option
114
- * (and the REST API's `tables.insert`) accepts it. `fields` recurses for
115
- * RECORD/STRUCT columns. */
135
+ * (and the REST API's `tables.insert`) accepts it: load-job INPUT, where the
136
+ * caller must state `name` and `type`. `fields` recurses for RECORD/STRUCT
137
+ * columns. The schema BigQuery reports back is `BigQueryResultSchemaField`. */
116
138
  export interface BigQuerySchemaField {
117
139
  name: string;
118
140
  type: string;
119
141
  mode?: "NULLABLE" | "REQUIRED" | "REPEATED";
120
142
  fields?: BigQuerySchemaField[];
121
143
  }
144
+ /** One column of a query's result schema, normalised from BigQuery's own
145
+ * dry-run statistics: `name` and `type` are guaranteed (a field missing
146
+ * either is rejected with `MalformedResultSchemaError`); `mode` is BigQuery's
147
+ * string as reported. `fields` recurses for RECORD/STRUCT columns. */
148
+ export interface BigQueryResultSchemaField {
149
+ name: string;
150
+ type: string;
151
+ mode?: string;
152
+ fields?: BigQueryResultSchemaField[];
153
+ }
122
154
  export interface BigQueryLoadOptions {
123
155
  sourceFormat?: "NEWLINE_DELIMITED_JSON" | "CSV" | "AVRO" | "PARQUET";
124
156
  /** Full destination-table schema. Required when the table doesn't already
@@ -182,6 +214,11 @@ export interface RunQueryOptions {
182
214
  * belt to that check's suspenders, since data can grow between a dry
183
215
  * run and the real job, or a caller can skip the dry run entirely. */
184
216
  maximumBytesBilled?: number;
217
+ /** Fails the job with a typed `JobTimeoutError` once it runs longer than
218
+ * this many milliseconds, wall-clock from job creation — set on the job's
219
+ * own `configuration.jobTimeoutMs` (BigQuery enforces it server-side),
220
+ * not a client-side `Promise.race`. */
221
+ jobTimeoutMs?: number;
185
222
  }
186
223
  export interface QueryPage<T = Record<string, unknown>> {
187
224
  rows: T[];
@@ -197,6 +234,10 @@ export interface EstimateQueryBytesOptions {
197
234
  /** When given, `estimateQueryBytes` throws `DryRunBudgetExceededError`
198
235
  * instead of returning once the estimate exceeds this many bytes. */
199
236
  maxBytesBilled?: number;
237
+ /** Same `configuration.jobTimeoutMs` enforcement as `RunQueryOptions` —
238
+ * applies to the dry-run job itself, not the (never-run) query it
239
+ * estimates. */
240
+ jobTimeoutMs?: number;
200
241
  }
201
242
  export interface DryRunEstimate {
202
243
  totalBytesProcessed: number;
@@ -211,6 +252,10 @@ export interface DryRunEstimate {
211
252
  * checks every entry against its own read-only table allowlist before
212
253
  * ever submitting the real job. */
213
254
  referencedTables?: BigQueryTableReference[];
255
+ /** BigQuery's own result schema for the query, typed directly from the
256
+ * dry-run job's `statistics.query.schema` — never inferred client-side.
257
+ * `undefined` only when BigQuery's dry-run statistics omitted it. */
258
+ schema?: BigQueryResultSchemaField[];
214
259
  }
215
260
  export interface ExtractTableToGCSInput {
216
261
  datasetId: string;
@@ -41,32 +41,63 @@
41
41
  * async (request) => recordReply(request.body),
42
42
  * );
43
43
  *
44
- * Usage — the MCP server script the dispatched agent's clean room actually runs
45
- * (typically `dist/chat/mcp/entry.js`, wired through a `.danxbot/config/mcp-servers/
46
- * *.yml`):
44
+ * Usage — the MCP server the dispatched agent's clean room actually runs (AK-34): a
45
+ * `.danxbot/config/mcp-servers/*.yml` launches the PUBLISHED `app-kit-chat-mcp` bin
46
+ * directly, no vendored copy and no local install in the app's own repo. This
47
+ * package publishes FIVE bins and none is named `app-kit`, so `npx` needs
48
+ * `--package=` to say which package to fetch, separately from which bin to run —
49
+ * `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` fails ("could not determine
50
+ * executable to run"); this is the form that actually works (verified against a real
51
+ * `npm pack` tarball run via `npx --package=<tgz>` from a clean directory with no
52
+ * `node_modules` — see `mcp/entry.test.ts`):
47
53
  *
48
- * import {
49
- * createChatMcpProtocol,
50
- * createChatMcpStdioServer,
51
- * createReplyTool,
52
- * } from "@flytedesk/app-kit/chat";
54
+ * server:
55
+ * command: npx
56
+ * args:
57
+ * - "-y"
58
+ * - "--package=@flytedesk/app-kit@6.0.0"
59
+ * - "app-kit-chat-mcp"
60
+ * - "--tools"
61
+ * - "${DANX_REPO_ROOT}/packages/audience-chat-mcp/src/chatTools.mjs"
53
62
  *
54
- * const protocol = createChatMcpProtocol({
55
- * serverName: "sms-app-chat",
56
- * tools: [
57
- * createReplyTool({ description: "...", deliver: postReplyToApi }),
58
- * // The extension point: register whatever app-specific tools the agent
59
- * // needs — each is just { name, description, inputSchema, handler }.
60
- * { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
61
- * ],
62
- * });
63
- * createChatMcpStdioServer({ protocol }).start();
63
+ * `--tools <path>` MUST be absolute — danxbot's cwd for this process is its own
64
+ * clean room, never assumed to equal the app's repo root, hence the
65
+ * `${DANX_REPO_ROOT}`-prefixed path above rather than a bare relative one.
66
+ *
67
+ * The path names the ONE thing this package cannot supply itself: the app's own
68
+ * tools module. That module must NOT `import` anything from `@flytedesk/app-kit` —
69
+ * a bare package import cannot resolve from danxbot's unbuilt dispatch clone (no
70
+ * `node_modules`). Instead, `app-kit-chat-mcp` INJECTS the pieces the module needs as
71
+ * a `ChatMcpKit` argument, and the module returns a (optionally wrapped)
72
+ * `ChatMcpProtocol` rather than a plain options bag:
73
+ *
74
+ * // chatTools.mjs — the app's own file, never vendored, never copied, never
75
+ * // imports @flytedesk/app-kit as a VALUE (a type-only `// @ts-check` JSDoc
76
+ * // reference to BuildChatServer/ChatMcpKit is fine — see mcp/entry.ts's header)
77
+ * export default function buildChatServer(kit) {
78
+ * return kit.createChatMcpProtocol({
79
+ * serverName: "sms-app-chat",
80
+ * tools: [
81
+ * kit.createReplyTool({ description: "...", deliver: postReplyToApi }),
82
+ * // The extension point: register whatever app-specific tools the agent
83
+ * // needs — each is just { name, description, inputSchema, handler }.
84
+ * { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
85
+ * ],
86
+ * });
87
+ * }
88
+ *
89
+ * `app-kit-chat-mcp` (`src/chat/mcp/entry.ts`, published as `dist/chat/mcp/entry.js`)
90
+ * loads that module, calls its default export with the kit, and starts the stdio
91
+ * loop on whatever `ChatMcpProtocol` it returns — see `entry.ts`'s own doc comment
92
+ * for the full contract, why it stays dependency-free, and how a consumer (e.g.
93
+ * media-planner's MP-169 `loggedProtocol`) wraps `handleMessage` before returning.
64
94
  */
65
- export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus } from "./launcher.js";
66
- export { callbackSecretMatches, requireCallbackSecret } from "./callback-secret.js";
95
+ export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus, } from "./launcher.js";
96
+ export { callbackSecretMatches, requireCallbackSecret, } from "./callback-secret.js";
67
97
  export { createChatMcpProtocol, createReplyTool, parseReplyArguments, FALLBACK_PROTOCOL_VERSION, JSON_RPC, } from "./mcp-protocol.js";
68
98
  export { createChatMcpStdioServer } from "./mcp-server.js";
69
99
  export { parseChatEnv, ChatEnvError } from "./env.js";
70
100
  export type { AppRegisteredTool, ChatEnv, ChatToolCallResult, ChatToolContent, DanxbotDispatchResult, DanxbotJobStatus, DanxbotLauncher, DanxbotLauncherOptions, } from "./types.js";
71
101
  export type { ChatMcpProtocol, ChatMcpProtocolOptions, ChatReplyPayload, ParseReplyArgumentsOptions, ReplyDeliverResult, ReplyToolOptions, } from "./mcp-protocol.js";
72
- export type { ChatMcpStdioServer, ChatMcpStdioServerOptions } from "./mcp-server.js";
102
+ export type { ChatMcpStdioServer, ChatMcpStdioServerOptions, } from "./mcp-server.js";
103
+ export type { BuildChatServer, ChatMcpKit } from "./mcp/entry.js";
@@ -41,29 +41,59 @@
41
41
  * async (request) => recordReply(request.body),
42
42
  * );
43
43
  *
44
- * Usage — the MCP server script the dispatched agent's clean room actually runs
45
- * (typically `dist/chat/mcp/entry.js`, wired through a `.danxbot/config/mcp-servers/
46
- * *.yml`):
44
+ * Usage — the MCP server the dispatched agent's clean room actually runs (AK-34): a
45
+ * `.danxbot/config/mcp-servers/*.yml` launches the PUBLISHED `app-kit-chat-mcp` bin
46
+ * directly, no vendored copy and no local install in the app's own repo. This
47
+ * package publishes FIVE bins and none is named `app-kit`, so `npx` needs
48
+ * `--package=` to say which package to fetch, separately from which bin to run —
49
+ * `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` fails ("could not determine
50
+ * executable to run"); this is the form that actually works (verified against a real
51
+ * `npm pack` tarball run via `npx --package=<tgz>` from a clean directory with no
52
+ * `node_modules` — see `mcp/entry.test.ts`):
47
53
  *
48
- * import {
49
- * createChatMcpProtocol,
50
- * createChatMcpStdioServer,
51
- * createReplyTool,
52
- * } from "@flytedesk/app-kit/chat";
54
+ * server:
55
+ * command: npx
56
+ * args:
57
+ * - "-y"
58
+ * - "--package=@flytedesk/app-kit@6.0.0"
59
+ * - "app-kit-chat-mcp"
60
+ * - "--tools"
61
+ * - "${DANX_REPO_ROOT}/packages/audience-chat-mcp/src/chatTools.mjs"
53
62
  *
54
- * const protocol = createChatMcpProtocol({
55
- * serverName: "sms-app-chat",
56
- * tools: [
57
- * createReplyTool({ description: "...", deliver: postReplyToApi }),
58
- * // The extension point: register whatever app-specific tools the agent
59
- * // needs — each is just { name, description, inputSchema, handler }.
60
- * { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
61
- * ],
62
- * });
63
- * createChatMcpStdioServer({ protocol }).start();
63
+ * `--tools <path>` MUST be absolute — danxbot's cwd for this process is its own
64
+ * clean room, never assumed to equal the app's repo root, hence the
65
+ * `${DANX_REPO_ROOT}`-prefixed path above rather than a bare relative one.
66
+ *
67
+ * The path names the ONE thing this package cannot supply itself: the app's own
68
+ * tools module. That module must NOT `import` anything from `@flytedesk/app-kit` —
69
+ * a bare package import cannot resolve from danxbot's unbuilt dispatch clone (no
70
+ * `node_modules`). Instead, `app-kit-chat-mcp` INJECTS the pieces the module needs as
71
+ * a `ChatMcpKit` argument, and the module returns a (optionally wrapped)
72
+ * `ChatMcpProtocol` rather than a plain options bag:
73
+ *
74
+ * // chatTools.mjs — the app's own file, never vendored, never copied, never
75
+ * // imports @flytedesk/app-kit as a VALUE (a type-only `// @ts-check` JSDoc
76
+ * // reference to BuildChatServer/ChatMcpKit is fine — see mcp/entry.ts's header)
77
+ * export default function buildChatServer(kit) {
78
+ * return kit.createChatMcpProtocol({
79
+ * serverName: "sms-app-chat",
80
+ * tools: [
81
+ * kit.createReplyTool({ description: "...", deliver: postReplyToApi }),
82
+ * // The extension point: register whatever app-specific tools the agent
83
+ * // needs — each is just { name, description, inputSchema, handler }.
84
+ * { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
85
+ * ],
86
+ * });
87
+ * }
88
+ *
89
+ * `app-kit-chat-mcp` (`src/chat/mcp/entry.ts`, published as `dist/chat/mcp/entry.js`)
90
+ * loads that module, calls its default export with the kit, and starts the stdio
91
+ * loop on whatever `ChatMcpProtocol` it returns — see `entry.ts`'s own doc comment
92
+ * for the full contract, why it stays dependency-free, and how a consumer (e.g.
93
+ * media-planner's MP-169 `loggedProtocol`) wraps `handleMessage` before returning.
64
94
  */
65
- export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus } from "./launcher.js";
66
- export { callbackSecretMatches, requireCallbackSecret } from "./callback-secret.js";
95
+ export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus, } from "./launcher.js";
96
+ export { callbackSecretMatches, requireCallbackSecret, } from "./callback-secret.js";
67
97
  export { createChatMcpProtocol, createReplyTool, parseReplyArguments, FALLBACK_PROTOCOL_VERSION, JSON_RPC, } from "./mcp-protocol.js";
68
98
  export { createChatMcpStdioServer } from "./mcp-server.js";
69
99
  export { parseChatEnv, ChatEnvError } from "./env.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,QAAQ,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6FG;AACH,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,QAAQ,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { createChatMcpProtocol, createReplyTool, parseReplyArguments, type ChatMcpProtocol } from "../mcp-protocol.js";
3
+ /**
4
+ * Everything a `--tools` module needs, injected rather than imported — see this
5
+ * file's own doc comment for why a runtime `import "@flytedesk/app-kit/chat"` from
6
+ * the module itself cannot work in danxbot's unbuilt dispatch clone.
7
+ */
8
+ export interface ChatMcpKit {
9
+ createReplyTool: typeof createReplyTool;
10
+ parseReplyArguments: typeof parseReplyArguments;
11
+ createChatMcpProtocol: typeof createChatMcpProtocol;
12
+ }
13
+ /** The contract a `--tools` module's default export must satisfy: given the kit,
14
+ * return a (possibly wrapped) `ChatMcpProtocol`. */
15
+ export type BuildChatServer = (kit: ChatMcpKit) => ChatMcpProtocol | Promise<ChatMcpProtocol>;
16
+ export declare class ChatMcpEntryError extends Error {
17
+ }
18
+ export interface ChatMcpEntryArgs {
19
+ /** Path to the app's tools module. MUST be absolute in real dispatch use (see this
20
+ * file's own doc comment) — resolved against `process.cwd()` only as a fallback
21
+ * for a genuinely relative path, which `path.resolve` leaves untouched when the
22
+ * input is already absolute. */
23
+ toolsPath: string;
24
+ }
25
+ /** Parse argv into `{toolsPath}`. Exported for direct unit testing. */
26
+ export declare function parseArgs(argv: string[]): ChatMcpEntryArgs;
27
+ /**
28
+ * Dynamically `import()` the app's tools module and validate its default export.
29
+ * Exported for direct unit testing (no process spawn needed to exercise the loading
30
+ * and validation logic — see entry.test.ts's own split between a fast in-process
31
+ * suite for this function and real stdio/npx spawn suites for the whole process).
32
+ */
33
+ export declare function loadBuildChatServer(toolsPath: string): Promise<BuildChatServer>;
34
+ /** Run the server: load the tools module, build the protocol (via the injected
35
+ * kit), and start stdio. Exported for direct unit testing of argument/validation
36
+ * failures without a process spawn. */
37
+ export declare function main(argv: string[]): Promise<void>;
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * app-kit-chat-mcp (AK-34) — a runnable stdio entry point for
4
+ * `@flytedesk/app-kit/chat`'s MCP server, so a consuming app's
5
+ * `.danxbot/config/mcp-servers/*.yml` can launch a real chat MCP server without
6
+ * vendoring a copy of `mcp-protocol.js`/`mcp-server.js` into its own repo.
7
+ *
8
+ * ## Launch form
9
+ *
10
+ * This package publishes FIVE bins and none is named `app-kit`, so npx needs
11
+ * `--package=` to say which package to fetch (separate from which bin to run):
12
+ *
13
+ * npx -y --package=@flytedesk/app-kit@<v> app-kit-chat-mcp --tools <abs path>
14
+ *
15
+ * `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` (no `--package=`) fails —
16
+ * verified against a real `npm pack` tarball run via `npx --package=<tgz>` from a
17
+ * clean directory with no `node_modules` (see entry.test.ts's "packed via npm pack,
18
+ * run via npx --package" suite).
19
+ *
20
+ * `--tools <path>` is resolved against `process.cwd()`, which danxbot sets to its
21
+ * OWN clean-room working directory — an app's yml MUST pass a
22
+ * `${DANX_REPO_ROOT}`-absolute path, never a bare relative one.
23
+ *
24
+ * ## The `--tools` module — kit injection, not a package import
25
+ *
26
+ * The module must NOT `import` `@flytedesk/app-kit` itself — that cannot resolve
27
+ * from danxbot's unbuilt dispatch clone (no `node_modules`), and would just move the
28
+ * vendoring problem this card closes into "the app re-implements createReplyTool".
29
+ * Instead this file injects a `ChatMcpKit` (`createReplyTool`, `parseReplyArguments`,
30
+ * `createChatMcpProtocol` — the same values `@flytedesk/app-kit/chat` exports) as
31
+ * the module's one argument:
32
+ *
33
+ * export default function build(kit) {
34
+ * return kit.createChatMcpProtocol({
35
+ * serverName: "my-app-chat",
36
+ * tools: [kit.createReplyTool({ description: "...", deliver: postReply }), ...],
37
+ * });
38
+ * }
39
+ *
40
+ * The default export returns a `ChatMcpProtocol` (`{handleMessage}`), not a plain
41
+ * options bag, so it can wrap the built protocol before returning (e.g.
42
+ * media-planner's MP-169 `loggedProtocol`). A module wanting type-checking can
43
+ * reference the exported `BuildChatServer`/`ChatMcpKit` TYPES via a `// @ts-check`
44
+ * JSDoc `@type` comment — type-only, erased, resolves fine from the app's own dev
45
+ * environment; it must never become a value import.
46
+ *
47
+ * ## Dependency-free at runtime
48
+ *
49
+ * Node builtins + this package's own `mcp-protocol.js`/`mcp-server.js` (themselves
50
+ * dependency-free) + `../../cli/is-running-as-main.js` (also Node-builtins-only —
51
+ * deliberately not imported from `../../cli/sync-engine.js`, which pulls in
52
+ * `cross-spawn` for its own Prisma-shelling subcommands).
53
+ */
54
+ import { pathToFileURL } from "node:url";
55
+ import { resolve as resolvePath } from "node:path";
56
+ import { createChatMcpProtocol, createReplyTool, parseReplyArguments, } from "../mcp-protocol.js";
57
+ import { createChatMcpStdioServer } from "../mcp-server.js";
58
+ import { isRunningAsMain } from "../../cli/is-running-as-main.js";
59
+ const KIT = {
60
+ createReplyTool,
61
+ parseReplyArguments,
62
+ createChatMcpProtocol,
63
+ };
64
+ export class ChatMcpEntryError extends Error {
65
+ }
66
+ /** Parse argv into `{toolsPath}`. Exported for direct unit testing. */
67
+ export function parseArgs(argv) {
68
+ let toolsPath;
69
+ for (let i = 0; i < argv.length; i += 1) {
70
+ const arg = argv[i];
71
+ if (arg === "--tools") {
72
+ toolsPath = argv[i + 1];
73
+ i += 1;
74
+ }
75
+ else {
76
+ throw new ChatMcpEntryError(`app-kit-chat-mcp: unrecognized argument "${arg}"`);
77
+ }
78
+ }
79
+ if (toolsPath === undefined || toolsPath === "") {
80
+ throw new ChatMcpEntryError("app-kit-chat-mcp: missing required --tools <absolute path to the app's tools module>");
81
+ }
82
+ return { toolsPath };
83
+ }
84
+ /**
85
+ * Dynamically `import()` the app's tools module and validate its default export.
86
+ * Exported for direct unit testing (no process spawn needed to exercise the loading
87
+ * and validation logic — see entry.test.ts's own split between a fast in-process
88
+ * suite for this function and real stdio/npx spawn suites for the whole process).
89
+ */
90
+ export async function loadBuildChatServer(toolsPath) {
91
+ const absolutePath = resolvePath(process.cwd(), toolsPath);
92
+ let mod;
93
+ try {
94
+ mod = await import(pathToFileURL(absolutePath).href);
95
+ }
96
+ catch (cause) {
97
+ throw new ChatMcpEntryError(`app-kit-chat-mcp: could not load tools module "${toolsPath}" (resolved to ` +
98
+ `${absolutePath}): ${cause instanceof Error ? cause.message : String(cause)}`);
99
+ }
100
+ const candidate = mod.default;
101
+ if (typeof candidate !== "function") {
102
+ throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}" must have a default export that is a ` +
103
+ `function returning a ChatMcpProtocol; got ${typeof candidate}.`);
104
+ }
105
+ return candidate;
106
+ }
107
+ /** Run the server: load the tools module, build the protocol (via the injected
108
+ * kit), and start stdio. Exported for direct unit testing of argument/validation
109
+ * failures without a process spawn. */
110
+ export async function main(argv) {
111
+ const { toolsPath } = parseArgs(argv);
112
+ const buildChatServer = await loadBuildChatServer(toolsPath);
113
+ let protocol;
114
+ try {
115
+ protocol = await buildChatServer(KIT);
116
+ }
117
+ catch (cause) {
118
+ throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}" threw while building its chat server: ` +
119
+ `${cause instanceof Error ? cause.message : String(cause)}`);
120
+ }
121
+ if (protocol === null ||
122
+ typeof protocol !== "object" ||
123
+ typeof protocol.handleMessage !== "function") {
124
+ throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}"'s default export must return a ` +
125
+ `ChatMcpProtocol ({handleMessage(message)}); got ${typeof protocol}.`);
126
+ }
127
+ createChatMcpStdioServer({ protocol }).start();
128
+ }
129
+ if (isRunningAsMain(import.meta.url)) {
130
+ main(process.argv.slice(2)).catch((err) => {
131
+ console.error(err instanceof ChatMcpEntryError ? err.message : err);
132
+ process.exitCode = 1;
133
+ });
134
+ }
135
+ //# sourceMappingURL=entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/chat/mcp/entry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function isRunningAsMain(moduleUrl: string): boolean;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Whether the module at `moduleUrl` (pass `import.meta.url`) is the one Node was
3
+ * invoked to run directly — the ESM successor to `require.main === module`, shared
4
+ * by every bin wrapper (trace-sync.ts/profile-sync.ts/flags-sync.ts/spa-server/cli.ts
5
+ * and src/chat/mcp/entry.ts) so there's one fix point instead of N copies of the
6
+ * same check. Node builtins ONLY (`node:fs`, `node:url`) — importing this file never
7
+ * drags in anything else, which is why src/chat/mcp/entry.ts imports it directly
8
+ * instead of importing `isRunningAsMain` from ./sync-engine.js: that file also pulls
9
+ * in `cross-spawn` (for its Prisma-shelling subcommands) at module scope, which would
10
+ * break entry.ts's own "Node builtins + this package's own chat modules only"
11
+ * dependency-free bar. sync-engine.ts re-exports this same function so its existing
12
+ * importers (trace-sync.ts et al.) are unaffected.
13
+ *
14
+ * A raw `import.meta.url === pathToFileURL(process.argv[1]).href` string comparison
15
+ * (the previous check) is NOT reliable: a package manager's bin shim can leave
16
+ * `argv[1]` pointing at a *symlink* while Node resolves the ESM module id through
17
+ * that symlink to the real target path for `import.meta.url` — so the two name the
18
+ * same file but compare unequal. This is not hypothetical: pnpm's
19
+ * `node_modules/<pkg>` is exactly such a symlink into its content-addressable
20
+ * store, and it reproduces on Windows for every real invocation path (`pnpm exec
21
+ * flags-sync init`, an npm-script `"flags-sync init"` run via `pnpm run`, etc.) —
22
+ * confirmed by instrumenting a real 0.3.0 install: `argv[1]` resolved to
23
+ * `...\node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js` (the symlinked
24
+ * path) while `import.meta.url` resolved to
25
+ * `...\node_modules\.pnpm\@flytedesk+app-kit@0.3.0_.../node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js`
26
+ * (the real store path). The mismatch meant `main()` was never called at all —
27
+ * every CLI entrypoint silently no-op'd: exit code 0, no output, nothing written.
28
+ *
29
+ * `realpathSync.native` (the real OS `realpath` syscall, not Node's pure-JS
30
+ * fallback — verified directly: the JS fallback leaves an on-disk casing mismatch
31
+ * unresolved, e.g. `RealFile.js` vs `realfile.js` compare unequal even though NTFS
32
+ * treats them as the same file, while `.native` correctly normalizes both to the
33
+ * same canonical path) resolves both sides to the same canonical filesystem path
34
+ * first — following symlinks AND normalizing casing — so the comparison is correct
35
+ * regardless of symlinks, path-separator style, or casing differences between how
36
+ * a shell/bin-shim sets `argv[1]` and how Node constructs `import.meta.url` for
37
+ * the same file.
38
+ */
39
+ import { realpathSync } from "node:fs";
40
+ import { fileURLToPath } from "node:url";
41
+ export function isRunningAsMain(moduleUrl) {
42
+ if (process.argv[1] === undefined)
43
+ return false;
44
+ try {
45
+ return (realpathSync.native(fileURLToPath(moduleUrl)) ===
46
+ realpathSync.native(process.argv[1]));
47
+ }
48
+ catch {
49
+ // Either path doesn't exist / isn't resolvable (e.g. a REPL or a bundler that
50
+ // gives import.meta.url a non-file URL) — definitely not "run directly" then.
51
+ return false;
52
+ }
53
+ }
54
+ //# sourceMappingURL=is-running-as-main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-running-as-main.js","sourceRoot":"","sources":["../../src/cli/is-running-as-main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,CACL,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC7C,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CACrC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;QAC9E,8EAA8E;QAC9E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}