@kansodata/kansodata-glue-plugin 0.1.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 (66) hide show
  1. package/AGENTS.md +30 -0
  2. package/README.md +119 -0
  3. package/dist/client/glueClientFactory.d.ts +3 -0
  4. package/dist/client/glueClientFactory.js +7 -0
  5. package/dist/client/glueClientFactory.js.map +1 -0
  6. package/dist/config/loadConfig.d.ts +7 -0
  7. package/dist/config/loadConfig.js +82 -0
  8. package/dist/config/loadConfig.js.map +1 -0
  9. package/dist/config/schema.d.ts +110 -0
  10. package/dist/config/schema.js +78 -0
  11. package/dist/config/schema.js.map +1 -0
  12. package/dist/domain/contracts.d.ts +65 -0
  13. package/dist/domain/contracts.js +2 -0
  14. package/dist/domain/contracts.js.map +1 -0
  15. package/dist/errors/glueErrors.d.ts +30 -0
  16. package/dist/errors/glueErrors.js +43 -0
  17. package/dist/errors/glueErrors.js.map +1 -0
  18. package/dist/guards/operationGuards.d.ts +4 -0
  19. package/dist/guards/operationGuards.js +28 -0
  20. package/dist/guards/operationGuards.js.map +1 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.js +4 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/manifest.d.ts +14 -0
  25. package/dist/manifest.js +15 -0
  26. package/dist/manifest.js.map +1 -0
  27. package/dist/openclawEntry.d.ts +8 -0
  28. package/dist/openclawEntry.js +41 -0
  29. package/dist/openclawEntry.js.map +1 -0
  30. package/dist/pluginMetadata.d.ts +5 -0
  31. package/dist/pluginMetadata.js +6 -0
  32. package/dist/pluginMetadata.js.map +1 -0
  33. package/dist/runtime/pluginRuntime.d.ts +22 -0
  34. package/dist/runtime/pluginRuntime.js +47 -0
  35. package/dist/runtime/pluginRuntime.js.map +1 -0
  36. package/dist/services/glueService.d.ts +79 -0
  37. package/dist/services/glueService.js +318 -0
  38. package/dist/services/glueService.js.map +1 -0
  39. package/dist/tools/catalog/catalogTools.d.ts +6 -0
  40. package/dist/tools/catalog/catalogTools.js +108 -0
  41. package/dist/tools/catalog/catalogTools.js.map +1 -0
  42. package/dist/tools/common.d.ts +4 -0
  43. package/dist/tools/common.js +19 -0
  44. package/dist/tools/common.js.map +1 -0
  45. package/dist/tools/registry.d.ts +3 -0
  46. package/dist/tools/registry.js +6 -0
  47. package/dist/tools/registry.js.map +1 -0
  48. package/dist/tools/triggers/triggerTools.d.ts +6 -0
  49. package/dist/tools/triggers/triggerTools.js +157 -0
  50. package/dist/tools/triggers/triggerTools.js.map +1 -0
  51. package/dist/tools/workflows/workflowTools.d.ts +6 -0
  52. package/dist/tools/workflows/workflowTools.js +106 -0
  53. package/dist/tools/workflows/workflowTools.js.map +1 -0
  54. package/dist/utils/sanitize.d.ts +4 -0
  55. package/dist/utils/sanitize.js +34 -0
  56. package/dist/utils/sanitize.js.map +1 -0
  57. package/docs/architecture.md +33 -0
  58. package/docs/roadmap.md +23 -0
  59. package/docs/rollback.md +34 -0
  60. package/docs/threat-model.md +31 -0
  61. package/docs/usage.md +70 -0
  62. package/openclaw.plugin.json +73 -0
  63. package/package.json +57 -0
  64. package/skills/kansodata-glue-operator/SKILL.md +47 -0
  65. package/skills/kansodata-glue-operator/docs/examples.md +44 -0
  66. package/skills/kansodata-glue-operator/docs/scope.md +37 -0
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ import { parseWithSchema } from "../common.js";
3
+ const GetCatalogTableInputSchema = z
4
+ .object({
5
+ databaseName: z.string().trim().min(1),
6
+ tableName: z.string().trim().min(1)
7
+ })
8
+ .strict();
9
+ const ListCatalogTablesInputSchema = z
10
+ .object({
11
+ databaseName: z.string().trim().min(1),
12
+ expression: z.string().trim().min(1).optional(),
13
+ maxResults: z.number().int().positive().max(100).default(25)
14
+ })
15
+ .strict();
16
+ const GetCatalogPartitionsSampleInputSchema = z
17
+ .object({
18
+ databaseName: z.string().trim().min(1),
19
+ tableName: z.string().trim().min(1),
20
+ expression: z.string().trim().min(1).optional(),
21
+ sampleSize: z.number().int().positive().max(25).optional()
22
+ })
23
+ .strict();
24
+ const ExplainPartitioningInputSchema = z
25
+ .object({
26
+ databaseName: z.string().trim().min(1),
27
+ tableName: z.string().trim().min(1),
28
+ sampleSize: z.number().int().positive().max(25).optional()
29
+ })
30
+ .strict();
31
+ export const getCatalogTableTool = {
32
+ name: "glue_get_catalog_table",
33
+ description: "Get sanitized Glue Catalog table metadata for ETL diagnosis.",
34
+ inputSchema: { type: "object", required: ["databaseName", "tableName"] },
35
+ parseInput: (input) => parseWithSchema(GetCatalogTableInputSchema, input),
36
+ execute: async (context, input) => {
37
+ const parsed = GetCatalogTableInputSchema.parse(input);
38
+ const table = await context.glueService.getCatalogTable(parsed.databaseName, parsed.tableName);
39
+ return {
40
+ summary: `Catalog table ${parsed.databaseName}.${parsed.tableName} loaded.`,
41
+ data: {
42
+ table
43
+ }
44
+ };
45
+ }
46
+ };
47
+ export const listCatalogTablesTool = {
48
+ name: "glue_list_catalog_tables",
49
+ description: "List catalog tables using bounded output and optional expression filter.",
50
+ inputSchema: { type: "object", required: ["databaseName"] },
51
+ parseInput: (input) => parseWithSchema(ListCatalogTablesInputSchema, input),
52
+ execute: async (context, input) => {
53
+ const parsed = ListCatalogTablesInputSchema.parse(input);
54
+ const tables = await context.glueService.listCatalogTables(parsed);
55
+ return {
56
+ summary: `Retrieved ${tables.length} table definitions from ${parsed.databaseName}.`,
57
+ data: {
58
+ tables
59
+ }
60
+ };
61
+ }
62
+ };
63
+ export const getCatalogPartitionsSampleTool = {
64
+ name: "glue_get_catalog_partitions_sample",
65
+ description: "Sample Glue Catalog partitions safely for ETL predicate diagnosis.",
66
+ inputSchema: { type: "object", required: ["databaseName", "tableName"] },
67
+ parseInput: (input) => parseWithSchema(GetCatalogPartitionsSampleInputSchema, input),
68
+ execute: async (context, input) => {
69
+ const parsed = GetCatalogPartitionsSampleInputSchema.parse(input);
70
+ const sampleSize = parsed.sampleSize ?? context.config.maxPartitionSampleSize;
71
+ const partitions = await context.glueService.getCatalogPartitionsSample({
72
+ databaseName: parsed.databaseName,
73
+ tableName: parsed.tableName,
74
+ expression: parsed.expression,
75
+ sampleSize
76
+ });
77
+ return {
78
+ summary: `Retrieved ${partitions.length} sampled partitions from ${parsed.databaseName}.${parsed.tableName}.`,
79
+ data: {
80
+ sampleSize,
81
+ partitions
82
+ }
83
+ };
84
+ }
85
+ };
86
+ export const explainPartitioningTool = {
87
+ name: "glue_explain_partitioning",
88
+ description: "Explain partitioning posture for from_catalog / push_down_predicate diagnostics.",
89
+ inputSchema: { type: "object", required: ["databaseName", "tableName"] },
90
+ parseInput: (input) => parseWithSchema(ExplainPartitioningInputSchema, input),
91
+ execute: async (context, input) => {
92
+ const parsed = ExplainPartitioningInputSchema.parse(input);
93
+ const explanation = await context.glueService.explainPartitioning(parsed.databaseName, parsed.tableName, parsed.sampleSize ?? context.config.maxPartitionSampleSize);
94
+ return {
95
+ summary: `Partitioning analysis generated for ${parsed.databaseName}.${parsed.tableName}.`,
96
+ data: {
97
+ explanation
98
+ }
99
+ };
100
+ }
101
+ };
102
+ export const catalogTools = [
103
+ getCatalogTableTool,
104
+ listCatalogTablesTool,
105
+ getCatalogPartitionsSampleTool,
106
+ explainPartitioningTool
107
+ ];
108
+ //# sourceMappingURL=catalogTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogTools.js","sourceRoot":"","sources":["../../../src/tools/catalog/catalogTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { z } from "zod";
2
+ import type { GluePluginConfig } from "../config/schema.js";
3
+ export declare const parseWithSchema: <T>(schema: z.ZodType<T>, payload: unknown) => T;
4
+ export declare const assertWriteSafe: (config: GluePluginConfig, name: string, type: "workflow" | "trigger", payload: unknown) => void;
@@ -0,0 +1,19 @@
1
+ import { InvalidInputError } from "../errors/glueErrors.js";
2
+ import { assertNameAllowedByPrefix, assertNoSecretsInPayload } from "../guards/operationGuards.js";
3
+ export const parseWithSchema = (schema, payload) => {
4
+ const parsed = schema.safeParse(payload);
5
+ if (!parsed.success) {
6
+ throw new InvalidInputError("Invalid tool input.", {
7
+ details: parsed.error.issues.map((issue) => ({
8
+ path: issue.path.join("."),
9
+ message: issue.message
10
+ }))
11
+ });
12
+ }
13
+ return parsed.data;
14
+ };
15
+ export const assertWriteSafe = (config, name, type, payload) => {
16
+ assertNoSecretsInPayload(payload);
17
+ assertNameAllowedByPrefix(config, name, type);
18
+ };
19
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/tools/common.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,CAAC,MAAM,eAAe,GAAG,CAAI,MAAoB,EAAE,OAAgB,EAAK,EAAE;IAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,iBAAiB,CAAC,qBAAqB,EAAE;YACjD,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC3C,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC,CAAC;SACJ,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,MAAwB,EACxB,IAAY,EACZ,IAA4B,EAC5B,OAAgB,EACV,EAAE;IACR,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAClC,yBAAyB,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ToolDefinition, ToolName } from "../domain/contracts.js";
2
+ export declare const allTools: readonly [ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>];
3
+ export declare const toolRegistry: ReadonlyMap<ToolName, ToolDefinition<unknown>>;
@@ -0,0 +1,6 @@
1
+ import { catalogTools } from "./catalog/catalogTools.js";
2
+ import { triggerTools } from "./triggers/triggerTools.js";
3
+ import { workflowTools } from "./workflows/workflowTools.js";
4
+ export const allTools = [...workflowTools, ...triggerTools, ...catalogTools];
5
+ export const toolRegistry = new Map(allTools.map((tool) => [tool.name, tool]));
6
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,YAAY,EAAE,GAAG,YAAY,CAAU,CAAC;AAEtF,MAAM,CAAC,MAAM,YAAY,GAAmD,IAAI,GAAG,CACjF,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAA+B,CAAC,CAAC,CACrE,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ToolDefinition } from "../../domain/contracts.js";
2
+ export declare const listTriggersTool: ToolDefinition<unknown>;
3
+ export declare const getTriggerTool: ToolDefinition<unknown>;
4
+ export declare const createTriggerTool: ToolDefinition<unknown>;
5
+ export declare const updateTriggerTool: ToolDefinition<unknown>;
6
+ export declare const triggerTools: readonly [ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>];
@@ -0,0 +1,157 @@
1
+ import { z } from "zod";
2
+ import { assertWriteSafe, parseWithSchema } from "../common.js";
3
+ const TriggerActionSchema = z
4
+ .object({
5
+ jobName: z.string().trim().min(1),
6
+ timeout: z.number().int().positive().max(2880).optional()
7
+ })
8
+ .strict();
9
+ const TriggerPredicateConditionSchema = z
10
+ .object({
11
+ logicalOperator: z.enum(["EQUALS"]).default("EQUALS"),
12
+ jobName: z.string().trim().min(1),
13
+ state: z.enum(["SUCCEEDED", "FAILED", "TIMEOUT", "STOPPED"])
14
+ })
15
+ .strict();
16
+ const TriggerPredicateSchema = z
17
+ .object({
18
+ logical: z.enum(["AND", "ANY"]).optional(),
19
+ conditions: z.array(TriggerPredicateConditionSchema).min(1)
20
+ })
21
+ .strict();
22
+ const ListTriggersInputSchema = z
23
+ .object({
24
+ limit: z.number().int().positive().max(100).default(25)
25
+ })
26
+ .strict();
27
+ const GetTriggerInputSchema = z
28
+ .object({
29
+ name: z.string().trim().min(1)
30
+ })
31
+ .strict();
32
+ const CreateTriggerInputSchema = z
33
+ .object({
34
+ name: z.string().trim().min(1),
35
+ type: z.enum(["SCHEDULED", "CONDITIONAL", "ON_DEMAND", "EVENT"]),
36
+ workflowName: z.string().trim().min(1).optional(),
37
+ schedule: z.string().trim().min(1).optional(),
38
+ description: z.string().trim().max(1024).optional(),
39
+ startOnCreation: z.boolean().optional(),
40
+ actions: z.array(TriggerActionSchema).min(1).max(20),
41
+ predicate: TriggerPredicateSchema.optional(),
42
+ dryRun: z.boolean().optional()
43
+ })
44
+ .strict();
45
+ const UpdateTriggerInputSchema = z
46
+ .object({
47
+ name: z.string().trim().min(1),
48
+ schedule: z.string().trim().min(1).optional(),
49
+ description: z.string().trim().max(1024).optional(),
50
+ actions: z.array(TriggerActionSchema).min(1).max(20).optional(),
51
+ predicate: TriggerPredicateSchema.optional(),
52
+ dryRun: z.boolean().optional()
53
+ })
54
+ .strict()
55
+ .refine((input) => input.schedule !== undefined ||
56
+ input.description !== undefined ||
57
+ input.actions !== undefined ||
58
+ input.predicate !== undefined, "At least one mutable field is required for update.");
59
+ const toActions = (actions) => actions.map((action) => ({
60
+ JobName: action.jobName,
61
+ Timeout: action.timeout
62
+ }));
63
+ const toPredicate = (predicate) => predicate
64
+ ? {
65
+ Logical: predicate.logical,
66
+ Conditions: predicate.conditions.map((condition) => ({
67
+ LogicalOperator: condition.logicalOperator ?? "EQUALS",
68
+ JobName: condition.jobName,
69
+ State: condition.state
70
+ }))
71
+ }
72
+ : undefined;
73
+ export const listTriggersTool = {
74
+ name: "glue_list_triggers",
75
+ description: "List AWS Glue triggers with bounded result size.",
76
+ inputSchema: { type: "object", properties: { limit: { type: "integer" } } },
77
+ parseInput: (input) => parseWithSchema(ListTriggersInputSchema, input),
78
+ execute: async (context, input) => {
79
+ const parsed = ListTriggersInputSchema.parse(input);
80
+ const triggers = await context.glueService.listTriggers(parsed.limit);
81
+ return {
82
+ summary: `Retrieved ${triggers.length} trigger names.`,
83
+ data: {
84
+ triggers
85
+ }
86
+ };
87
+ }
88
+ };
89
+ export const getTriggerTool = {
90
+ name: "glue_get_trigger",
91
+ description: "Get a sanitized trigger view for operational analysis.",
92
+ inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] },
93
+ parseInput: (input) => parseWithSchema(GetTriggerInputSchema, input),
94
+ execute: async (context, input) => {
95
+ const parsed = GetTriggerInputSchema.parse(input);
96
+ const trigger = await context.glueService.getTrigger(parsed.name);
97
+ return {
98
+ summary: `Trigger ${parsed.name} loaded.`,
99
+ data: {
100
+ trigger
101
+ }
102
+ };
103
+ }
104
+ };
105
+ export const createTriggerTool = {
106
+ name: "glue_create_trigger",
107
+ description: "Create Glue trigger with guarded payload and prefix checks.",
108
+ inputSchema: { type: "object" },
109
+ parseInput: (input) => parseWithSchema(CreateTriggerInputSchema, input),
110
+ execute: async (context, input) => {
111
+ const parsed = CreateTriggerInputSchema.parse(input);
112
+ assertWriteSafe(context.config, parsed.name, "trigger", parsed);
113
+ const trigger = await context.glueService.createTrigger({
114
+ name: parsed.name,
115
+ type: parsed.type,
116
+ workflowName: parsed.workflowName,
117
+ schedule: parsed.schedule,
118
+ description: parsed.description,
119
+ startOnCreation: parsed.startOnCreation,
120
+ actions: toActions(parsed.actions),
121
+ predicate: toPredicate(parsed.predicate),
122
+ dryRun: parsed.dryRun
123
+ });
124
+ return {
125
+ summary: `Trigger ${parsed.name} ${parsed.dryRun ?? context.config.dryRunByDefault ? "validated in dry-run" : "created"}.`,
126
+ data: {
127
+ trigger
128
+ }
129
+ };
130
+ }
131
+ };
132
+ export const updateTriggerTool = {
133
+ name: "glue_update_trigger",
134
+ description: "Safely update selected trigger fields on existing Glue resources.",
135
+ inputSchema: { type: "object" },
136
+ parseInput: (input) => parseWithSchema(UpdateTriggerInputSchema, input),
137
+ execute: async (context, input) => {
138
+ const parsed = UpdateTriggerInputSchema.parse(input);
139
+ assertWriteSafe(context.config, parsed.name, "trigger", parsed);
140
+ const trigger = await context.glueService.updateTrigger({
141
+ name: parsed.name,
142
+ schedule: parsed.schedule,
143
+ description: parsed.description,
144
+ actions: parsed.actions ? toActions(parsed.actions) : undefined,
145
+ predicate: toPredicate(parsed.predicate),
146
+ dryRun: parsed.dryRun
147
+ });
148
+ return {
149
+ summary: `Trigger ${parsed.name} ${parsed.dryRun ?? context.config.dryRunByDefault ? "validated in dry-run" : "updated"}.`,
150
+ data: {
151
+ trigger
152
+ }
153
+ };
154
+ }
155
+ };
156
+ export const triggerTools = [listTriggersTool, getTriggerTool, createTriggerTool, updateTriggerTool];
157
+ //# sourceMappingURL=triggerTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"triggerTools.js","sourceRoot":"","sources":["../../../src/tools/triggers/triggerTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { ToolDefinition } from "../../domain/contracts.js";
2
+ export declare const listWorkflowsTool: ToolDefinition<unknown>;
3
+ export declare const getWorkflowTool: ToolDefinition<unknown>;
4
+ export declare const createWorkflowTool: ToolDefinition<unknown>;
5
+ export declare const updateWorkflowTool: ToolDefinition<unknown>;
6
+ export declare const workflowTools: readonly [ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>, ToolDefinition<unknown>];
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ import { assertWriteSafe, parseWithSchema } from "../common.js";
3
+ const ListWorkflowsInputSchema = z
4
+ .object({
5
+ limit: z.number().int().positive().max(100).default(25)
6
+ })
7
+ .strict();
8
+ const GetWorkflowInputSchema = z
9
+ .object({
10
+ name: z.string().trim().min(1)
11
+ })
12
+ .strict();
13
+ const CreateWorkflowInputSchema = z
14
+ .object({
15
+ name: z.string().trim().min(1),
16
+ description: z.string().trim().max(1024).optional(),
17
+ maxConcurrentRuns: z.number().int().positive().max(100).optional(),
18
+ defaultRunProperties: z.record(z.string(), z.string()).optional(),
19
+ dryRun: z.boolean().optional()
20
+ })
21
+ .strict();
22
+ const UpdateWorkflowInputSchema = z
23
+ .object({
24
+ name: z.string().trim().min(1),
25
+ description: z.string().trim().max(1024).optional(),
26
+ maxConcurrentRuns: z.number().int().positive().max(100).optional(),
27
+ defaultRunProperties: z.record(z.string(), z.string()).optional(),
28
+ dryRun: z.boolean().optional()
29
+ })
30
+ .strict()
31
+ .refine((input) => input.description !== undefined ||
32
+ input.maxConcurrentRuns !== undefined ||
33
+ input.defaultRunProperties !== undefined, "At least one mutable field is required for update.");
34
+ export const listWorkflowsTool = {
35
+ name: "glue_list_workflows",
36
+ description: "List AWS Glue workflows with controlled result size.",
37
+ inputSchema: { type: "object", properties: { limit: { type: "integer" } } },
38
+ parseInput: (input) => parseWithSchema(ListWorkflowsInputSchema, input),
39
+ execute: async (context, input) => {
40
+ const parsed = ListWorkflowsInputSchema.parse(input);
41
+ const workflows = await context.glueService.listWorkflows(parsed.limit);
42
+ return {
43
+ summary: `Retrieved ${workflows.length} workflow names.`,
44
+ data: {
45
+ workflows
46
+ }
47
+ };
48
+ }
49
+ };
50
+ export const getWorkflowTool = {
51
+ name: "glue_get_workflow",
52
+ description: "Get a Glue workflow view suitable for operational diagnostics.",
53
+ inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] },
54
+ parseInput: (input) => parseWithSchema(GetWorkflowInputSchema, input),
55
+ execute: async (context, input) => {
56
+ const parsed = GetWorkflowInputSchema.parse(input);
57
+ const workflow = await context.glueService.getWorkflow(parsed.name);
58
+ return {
59
+ summary: `Workflow ${parsed.name} loaded.`,
60
+ data: {
61
+ workflow
62
+ }
63
+ };
64
+ }
65
+ };
66
+ export const createWorkflowTool = {
67
+ name: "glue_create_workflow",
68
+ description: "Create a Glue workflow under strict prefix and payload safety constraints.",
69
+ inputSchema: { type: "object" },
70
+ parseInput: (input) => parseWithSchema(CreateWorkflowInputSchema, input),
71
+ execute: async (context, input) => {
72
+ const parsed = CreateWorkflowInputSchema.parse(input);
73
+ assertWriteSafe(context.config, parsed.name, "workflow", parsed);
74
+ const workflow = await context.glueService.createWorkflow(parsed);
75
+ return {
76
+ summary: `Workflow ${parsed.name} ${parsed.dryRun ?? context.config.dryRunByDefault ? "validated in dry-run" : "created"}.`,
77
+ data: {
78
+ workflow
79
+ }
80
+ };
81
+ }
82
+ };
83
+ export const updateWorkflowTool = {
84
+ name: "glue_update_workflow",
85
+ description: "Safely update selected fields on an existing Glue workflow.",
86
+ inputSchema: { type: "object" },
87
+ parseInput: (input) => parseWithSchema(UpdateWorkflowInputSchema, input),
88
+ execute: async (context, input) => {
89
+ const parsed = UpdateWorkflowInputSchema.parse(input);
90
+ assertWriteSafe(context.config, parsed.name, "workflow", parsed);
91
+ const workflow = await context.glueService.updateWorkflow(parsed);
92
+ return {
93
+ summary: `Workflow ${parsed.name} ${parsed.dryRun ?? context.config.dryRunByDefault ? "validated in dry-run" : "updated"}.`,
94
+ data: {
95
+ workflow
96
+ }
97
+ };
98
+ }
99
+ };
100
+ export const workflowTools = [
101
+ listWorkflowsTool,
102
+ getWorkflowTool,
103
+ createWorkflowTool,
104
+ updateWorkflowTool
105
+ ];
106
+ //# sourceMappingURL=workflowTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowTools.js","sourceRoot":"","sources":["../../../src/tools/workflows/workflowTools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { GluePluginError } from "../errors/glueErrors.js";
2
+ export declare const sanitizeMessage: (message: string) => string;
3
+ export declare const toSanitizedError: (error: unknown) => GluePluginError;
4
+ export declare const hasPotentialSecret: (value: unknown) => boolean;
@@ -0,0 +1,34 @@
1
+ import { GluePluginError, RemoteServiceError } from "../errors/glueErrors.js";
2
+ const SECRET_PATTERN = /\b(password|passwd|token|secret|api[_-]?key|client[_-]?secret|private[_-]?key|authorization)\b/i;
3
+ const normalizeErrorMessage = (message) => message.replace(/[^\x20-\x7E]/g, "").slice(0, 300);
4
+ const scrubSecretsFromText = (text) => text
5
+ .split(/\s+/)
6
+ .map((chunk) => (SECRET_PATTERN.test(chunk) ? "[REDACTED]" : chunk))
7
+ .join(" ");
8
+ export const sanitizeMessage = (message) => scrubSecretsFromText(normalizeErrorMessage(message));
9
+ export const toSanitizedError = (error) => {
10
+ if (error instanceof GluePluginError) {
11
+ return error;
12
+ }
13
+ if (error instanceof Error) {
14
+ return new RemoteServiceError(sanitizeMessage(error.message), undefined, error);
15
+ }
16
+ return new RemoteServiceError("Unexpected remote error.");
17
+ };
18
+ export const hasPotentialSecret = (value) => {
19
+ if (typeof value === "string") {
20
+ return SECRET_PATTERN.test(value);
21
+ }
22
+ if (Array.isArray(value)) {
23
+ return value.some((item) => hasPotentialSecret(item));
24
+ }
25
+ if (value && typeof value === "object") {
26
+ for (const [key, objectValue] of Object.entries(value)) {
27
+ if (SECRET_PATTERN.test(key) || hasPotentialSecret(objectValue)) {
28
+ return true;
29
+ }
30
+ }
31
+ }
32
+ return false;
33
+ };
34
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../../src/utils/sanitize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,cAAc,GAClB,iGAAiG,CAAC;AAEpG,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAU,EAAE,CACxD,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAErD,MAAM,oBAAoB,GAAG,CAAC,IAAY,EAAU,EAAE,CACpD,IAAI;KACD,KAAK,CAAC,KAAK,CAAC;KACZ,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;KACnE,IAAI,CAAC,GAAG,CAAC,CAAC;AAEf,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAe,EAAU,EAAE,CACzD,oBAAoB,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAc,EAAmB,EAAE;IAClE,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,IAAI,kBAAkB,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,kBAAkB,CAAC,0BAA0B,CAAC,CAAC;AAC5D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAAW,EAAE;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ # Architecture
2
+
3
+ ## Design Goals
4
+ - Clean and modular runtime for controlled AWS Glue operations.
5
+ - Explicit separation of plugin runtime, configuration, services, tools, guards, and skill companion.
6
+ - Fail-closed defaults for configuration and write operations.
7
+
8
+ ## Module Boundaries
9
+ - `src/openclawEntry.ts`: official OpenClaw plugin SDK entrypoint (`definePluginEntry`) and tool registration (`api.registerTool`).
10
+ - `openclaw.plugin.json`: plugin manifest (`id`, `configSchema`, `contracts`, `skills`).
11
+ - `src/runtime/pluginRuntime.ts`: runtime entrypoint, manifest wiring, dispatch.
12
+ - `src/config/schema.ts`: canonical config model + OpenClaw JSON schema derivation source.
13
+ - `src/config/loadConfig.ts`: config precedence resolution (`OpenClaw > env > defaults`) + fail-closed validation.
14
+ - `src/client/*`: AWS SDK client creation.
15
+ - `src/services/glueService.ts`: AWS Glue adapter, safe update semantics, output shaping.
16
+ - `src/tools/*`: tool definitions and input contracts.
17
+ - `src/guards/*`: write-surface restrictions and payload safety checks.
18
+ - `src/errors/*`: typed error hierarchy for stable operator behavior.
19
+ - `skills/kansodata-glue-operator/*`: operational governance for agent usage.
20
+
21
+ ## Call Flow
22
+ 1. OpenClaw loads extension from `package.json.openclaw.extensions`.
23
+ 2. `src/openclawEntry.ts` registers plugin tools using official SDK APIs.
24
+ 3. Runtime resolves config with explicit precedence (`OpenClaw -> env -> defaults`) and strict validation.
25
+ 4. Tool dispatch validates input with schema.
26
+ 5. Guards enforce secrets/prefix/read-before-write constraints.
27
+ 6. Service executes SDK call (or dry-run projection).
28
+ 7. Output is normalized into concise operational structures.
29
+ 8. Errors are sanitized and mapped to typed categories.
30
+
31
+ ## Scalability Path
32
+ - V2 can add read-only Job/Crawler diagnostics as new tools without touching write guardrails.
33
+ - V3 can add controlled write operations via new service methods + dedicated guards per resource type.
@@ -0,0 +1,23 @@
1
+ # Roadmap
2
+
3
+ ## V1 (Current)
4
+ - OpenClaw plugin bootstrap with strict TS architecture.
5
+ - Controlled write subset:
6
+ - workflows: create/update
7
+ - triggers: create/update
8
+ - Catalog diagnostics:
9
+ - table metadata
10
+ - partition samples
11
+ - partitioning explanation for `from_catalog` / `push_down_predicate`.
12
+ - Companion operator skill + hardening docs + unit tests.
13
+
14
+ ## V2 (Planned)
15
+ - Read-only diagnostics for Glue Jobs and Crawlers.
16
+ - Richer workflow/trigger explainers (graph consistency checks, trigger chain hints).
17
+ - Expanded rule set for catalog quality checks.
18
+ - Optional integration tests in isolated environment.
19
+
20
+ ## V3 (Planned)
21
+ - Controlled write operations for Jobs/Crawlers with dedicated guardrails.
22
+ - Change-plan preview and approval token flow.
23
+ - Policy packs per environment/team (dev/stage/prod).
@@ -0,0 +1,34 @@
1
+ # Rollback Plan
2
+
3
+ ## Scope
4
+ Rollback for the V1 atomic correction that:
5
+ - narrows `glue_update_trigger` mutable fields to AWS-supported update fields,
6
+ - replaces custom runtime wiring with an official OpenClaw SDK entrypoint,
7
+ - aligns manifest/package skill discovery metadata,
8
+ - makes build clean script portable,
9
+ - aligns OpenClaw declared config schema with runtime-effective config precedence.
10
+
11
+ ## Rollback Procedure
12
+
13
+ ### After push (preferred)
14
+ 1. Identify the correction commit SHA.
15
+ 2. Revert it with a non-destructive commit:
16
+ - `git revert <commit-sha>`
17
+ 3. Run validations:
18
+ - `npm run lint`
19
+ - `npm run typecheck`
20
+ - `npm run build`
21
+ - `npm test`
22
+ 4. Push revert commit.
23
+
24
+ ### Before push
25
+ 1. Inspect local diff: `git status`.
26
+ 2. Restore only files touched by this iteration:
27
+ - `git restore <file...>`
28
+ 3. Re-run validations.
29
+
30
+ ## Functional Guardrails During Rollback Window
31
+ - Keep `dryRunByDefault=true`.
32
+ - Keep `enforcePrefixAllowlist=true` and non-empty prefix list.
33
+ - Do not expand write surface beyond V1 workflows/triggers.
34
+ - Keep config resolution fail-closed when region is absent or allowlist enforcement is unsafe.
@@ -0,0 +1,31 @@
1
+ # Threat Model (V1)
2
+
3
+ ## Protected Assets
4
+ - AWS Glue orchestration state (workflows/triggers).
5
+ - Glue Catalog metadata quality used by ETL diagnostics.
6
+ - AWS credentials and sensitive payload fragments.
7
+
8
+ ## Main Threats
9
+ 1. Unauthorized or accidental writes outside intended resource namespace.
10
+ 2. Secret leakage through tool input/output or error surfaces.
11
+ 3. Unsafe updates that override existing definitions blindly.
12
+ 4. Ambiguous operations executed without sufficient baseline state.
13
+ 5. Excessive data exposure from raw AWS API payloads.
14
+
15
+ ## Hardening Decisions
16
+ - Prefix allowlist enforcement available and enabled by default.
17
+ - Dry-run enabled by default.
18
+ - Secret-like payload detection blocks operations (fail-closed).
19
+ - Update operations require read-before-write path and limited mutable fields.
20
+ - Structured error sanitization and redaction.
21
+ - Tool responses emit summarized, shaped objects only.
22
+
23
+ ## Residual Risks
24
+ - Existing Glue definitions may already include risky defaults outside plugin control.
25
+ - IAM policy drift can still break operations at runtime.
26
+ - Semantic correctness of user intent still depends on operator input quality.
27
+
28
+ ## Compensating Controls
29
+ - Use companion skill mandatory sequence (`read -> plan -> validate -> apply -> verify`).
30
+ - Keep writes scoped by naming conventions and explicit allowlist.
31
+ - Prefer dry-run during initial rollout in each environment.