@ted-galago/wave-cli 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ted-galago/wave-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "wave": "dist/index.js"
@@ -13,6 +13,7 @@
13
13
  "build": "tsup src/index.ts --format esm,cjs --dts --clean",
14
14
  "dev": "tsx src/index.ts",
15
15
  "test": "vitest run",
16
+ "verify:dev": "node scripts/verify-dev-api.mjs",
16
17
  "postinstall": "node scripts/postinstall-local-bin.mjs"
17
18
  },
18
19
  "engines": {
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { spawnSync } from "node:child_process";
4
+ import { resolve } from "node:path";
5
+
6
+ function loadDotEnv(filePath) {
7
+ const raw = readFileSync(filePath, "utf8");
8
+ const env = {};
9
+ for (const line of raw.split(/\r?\n/)) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed || trimmed.startsWith("#")) continue;
12
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
13
+ if (!match) continue;
14
+ const [, key, value] = match;
15
+ env[key] = value;
16
+ }
17
+ return env;
18
+ }
19
+
20
+ function runWave(args, env) {
21
+ const child = spawnSync("node", ["dist/index.js", ...args], {
22
+ env: { ...process.env, ...env },
23
+ encoding: "utf8"
24
+ });
25
+
26
+ const stdout = child.stdout?.trim() ?? "";
27
+ let parsed = null;
28
+ try {
29
+ parsed = JSON.parse(stdout);
30
+ } catch {
31
+ parsed = null;
32
+ }
33
+
34
+ return {
35
+ args,
36
+ code: child.status ?? 1,
37
+ stdout,
38
+ stderr: child.stderr?.trim() ?? "",
39
+ parsed
40
+ };
41
+ }
42
+
43
+ function firstId(parsed, field) {
44
+ const data = parsed?.data?.[field];
45
+ if (data?.data?.[0]?.id) return String(data.data[0].id);
46
+ if (data?.id) return String(data.id);
47
+ return undefined;
48
+ }
49
+
50
+ function classify(result) {
51
+ if (!result.parsed) {
52
+ return { ok: false, reason: "non_json_output" };
53
+ }
54
+ if (result.parsed.ok === true) {
55
+ return { ok: true, reason: "ok" };
56
+ }
57
+
58
+ const code = result.parsed?.error?.code ?? "unknown";
59
+ const message = result.parsed?.error?.message ?? "";
60
+ const details = JSON.stringify(result.parsed?.error?.details ?? {});
61
+ const hardFailures = [
62
+ "network_error",
63
+ "missing_auth",
64
+ "generic_error"
65
+ ];
66
+ if (hardFailures.includes(code)) {
67
+ return { ok: false, reason: code };
68
+ }
69
+ if (
70
+ details.includes("missingRequiredArguments") ||
71
+ details.includes("argumentNotAccepted") ||
72
+ details.includes("undefinedField") ||
73
+ details.includes("variableNotUsed")
74
+ ) {
75
+ return { ok: false, reason: "graphql_contract_error" };
76
+ }
77
+ if (code === "invalid_args" && message.includes("Missing required create fields")) {
78
+ return { ok: true, reason: "skipped_missing_parent_dependency" };
79
+ }
80
+ if (code === "invalid_args") {
81
+ return { ok: false, reason: code };
82
+ }
83
+ return { ok: true, reason: `reachable_${code}` };
84
+ }
85
+
86
+ function payload(root, attrs) {
87
+ return JSON.stringify({ [root]: attrs });
88
+ }
89
+
90
+ const root = process.cwd();
91
+ const envFile = resolve(root, ".env");
92
+ const dot = loadDotEnv(envFile);
93
+ const env = {
94
+ WAVE_API_BASE_URL: dot.WAVE_API_BASE_URL,
95
+ WAVE_API_TOKEN: dot.WAVE_API_TOKEN,
96
+ WAVE_ORGANIZATION_ID: dot.WAVE_ORGANIZATION_ID,
97
+ WAVE_AGENT_NAME: dot.WAVE_AGENT_NAME || "cli-verifier",
98
+ WAVE_AGENT_RUN_ID: `verify-${Date.now()}`,
99
+ WAVE_TIMEOUT_MS: dot.WAVE_TIMEOUT_MS || "10000"
100
+ };
101
+
102
+ const idLookup = {};
103
+ const listSeeds = [
104
+ ["projects", ["projects", "list", "--page", "1", "--per", "2"]],
105
+ ["tasks", ["tasks", "list", "--page", "1", "--per", "2"]],
106
+ ["rocks", ["rocks", "list", "--page", "1", "--per", "2"]],
107
+ ["meetings", ["meetings", "list", "--per", "2"]],
108
+ ["members", ["members", "list", "--page", "1", "--per", "2"]],
109
+ ["teams", ["teams", "list", "--page", "1", "--per", "2"]],
110
+ ["issues", ["issues", "list", "--page", "1", "--per", "2"]],
111
+ ["lists", ["lists", "list", "--page", "1", "--per", "2"]],
112
+ ["listItems", ["list-items", "list", "--page", "1", "--per", "2"]],
113
+ ["issueGroups", ["issue-groups", "list", "--page", "1", "--per", "2"]],
114
+ ["todoGroups", ["todo-groups", "list", "--page", "1", "--per", "2"]],
115
+ ["todos", ["todos", "list", "--page", "1", "--per", "2"]],
116
+ ["rockCollections", ["rock-collections", "list", "--page", "1", "--per", "2"]],
117
+ ["knowledge", ["knowledge", "list", "--page", "1", "--per", "2"]],
118
+ ["standUps", ["stand-ups", "list", "--page", "1", "--per", "2"]],
119
+ ["news", ["news", "list", "--page", "1", "--per", "2"]],
120
+ ["questions", ["questions", "list", "--page", "1", "--per", "2"]],
121
+ ["pulse", ["pulse", "list", "--page", "1", "--per", "2"]],
122
+ ["surveys", ["surveys", "list", "--page", "1", "--per", "2"]],
123
+ ["feedbacks", ["feedbacks", "list", "--page", "1", "--per", "2"]],
124
+ ["accountability", ["accountability", "list", "--page", "1", "--per", "2"]],
125
+ ["kpis", ["kpis", "list", "--page", "1", "--per", "2"]],
126
+ ["scorecardGroups", ["scorecard-groups", "list", "--page", "1", "--per", "2"]],
127
+ ["scorecards", ["scorecards", "list", "--page", "1", "--per", "2"]],
128
+ ["customers", ["customers", "list", "--page", "1", "--per", "2"]],
129
+ ["contacts", ["contacts", "list", "--page", "1", "--per", "2"]],
130
+ ["annualObjectives", ["foundation", "annual-objectives", "list", "--page", "1", "--per", "2"]],
131
+ ["quarterlyObjectives", ["foundation", "quarterly-objectives", "list", "--page", "1", "--per", "2"]]
132
+ ];
133
+
134
+ const results = [];
135
+ for (const [key, args] of listSeeds) {
136
+ const res = runWave(args, env);
137
+ results.push(res);
138
+ if (res.parsed?.ok) {
139
+ const field = args[0] === "foundation" ? args[1].replace(/-([a-z])/g, (_, c) => c.toUpperCase()) : args[0].replace(/-([a-z])/g, (_, c) => c.toUpperCase());
140
+ idLookup[key] = firstId(res.parsed, field);
141
+ }
142
+ }
143
+
144
+ const orgId = env.WAVE_ORGANIZATION_ID;
145
+ const projectId = idLookup.projects;
146
+ const taskId = idLookup.tasks;
147
+ const rockId = idLookup.rocks;
148
+ const meetingId = idLookup.meetings;
149
+ const memberId = idLookup.members;
150
+ const teamId = idLookup.teams;
151
+ const issueId = idLookup.issues;
152
+ const issueGroupId = idLookup.issueGroups;
153
+ const listId = idLookup.lists;
154
+ const listItemId = idLookup.listItems;
155
+ const todoGroupId = idLookup.todoGroups;
156
+ const todoId = idLookup.todos;
157
+ const rockCollectionId = idLookup.rockCollections;
158
+ const contentId = idLookup.knowledge;
159
+ const standUpId = idLookup.standUps;
160
+ const headlineId = idLookup.news;
161
+ const questionId = idLookup.questions;
162
+ const pulseId = idLookup.pulse;
163
+ const surveyId = idLookup.surveys;
164
+ const feedbackId = idLookup.feedbacks;
165
+ const responsibilityId = idLookup.accountability;
166
+ const kpiId = idLookup.kpis;
167
+ const measurableGroupId = idLookup.scorecardGroups;
168
+ const measurableId = idLookup.scorecards;
169
+ const customerId = idLookup.customers;
170
+ const contactId = idLookup.contacts;
171
+ const annualObjectiveId = idLookup.annualObjectives;
172
+ const quarterlyObjectiveId = idLookup.quarterlyObjectives;
173
+
174
+ const probes = [
175
+ ["projects", "show", "--id", projectId],
176
+ ["tasks", "show", "--id", taskId],
177
+ ["rocks", "show", "--id", rockId],
178
+ ["meetings", "show", "--id", meetingId],
179
+ ["members", "show", "--id", memberId],
180
+ ["teams", "show", "--id", teamId],
181
+ ["issues", "show", "--id", issueId],
182
+ ["lists", "show", "--id", listId],
183
+ ["list-items", "show", "--id", listItemId],
184
+ ["issue-groups", "show", "--id", issueGroupId],
185
+ ["todo-groups", "show", "--id", todoGroupId],
186
+ ["todos", "show", "--id", todoId],
187
+ ["rock-collections", "show", "--id", rockCollectionId],
188
+ ["knowledge", "show", "--id", contentId],
189
+ ["stand-ups", "show", "--id", standUpId],
190
+ ["news", "show", "--id", headlineId],
191
+ ["questions", "show", "--id", questionId],
192
+ ["pulse", "show", "--id", pulseId],
193
+ ["surveys", "show", "--id", surveyId],
194
+ ["feedbacks", "show", "--id", feedbackId],
195
+ ["accountability", "show", "--id", responsibilityId],
196
+ ["kpis", "show", "--id", kpiId],
197
+ ["scorecard-groups", "show", "--id", measurableGroupId],
198
+ ["scorecards", "show", "--id", measurableId],
199
+ ["customers", "show", "--id", customerId],
200
+ ["contacts", "show", "--id", contactId],
201
+ ["organizations", "show", "--id", orgId],
202
+ ["organizations", "meta-profile", "show", "--id", orgId],
203
+ ["organizations", "key-metric-meta-profile", "show", "--id", orgId],
204
+ ["foundation", "strategic-plans", "show", "--id", orgId],
205
+ ["foundation", "strategic-objectives", "show", "--id", orgId],
206
+ ["foundation", "annual-objectives", "show", "--id", annualObjectiveId],
207
+ ["foundation", "quarterly-objectives", "show", "--id", quarterlyObjectiveId],
208
+ ["projects", "create", "--data-json", payload("project", { name: "CLI Verify Project", status: "backlog", priority: "no_priority" })],
209
+ ["projects", "update", "--id", projectId, "--data-json", payload("project", { description: "verify-update" })],
210
+ ["tasks", "create", "--project-id", projectId, "--summary", "CLI verify task"],
211
+ ["tasks", "update", "--id", taskId, "--summary", "CLI verify task update"],
212
+ ["rocks", "update-status", "--id", rockId, "--status", "on_track"],
213
+ ["rocks", "create", "--data-json", payload("rock", { name: "CLI Verify Rock", rock_collection_id: rockCollectionId, status: "on_track", priority: "no_priority", rock_type: "individual" })],
214
+ ["rocks", "update", "--id", rockId, "--data-json", payload("rock", { description: "verify-update" })],
215
+ ["meetings", "notes", "--id", meetingId, "--content", "CLI verify notes"],
216
+ ["meetings", "create", "--data-json", payload("meeting", { name: "CLI Verify Meeting", status: "upcoming" })],
217
+ ["meetings", "update", "--id", meetingId, "--data-json", payload("meeting", { notes: "verify-update" })],
218
+ ["members", "create", "--data-json", payload("member", { email: `cli.verify.${Date.now()}@example.com` })],
219
+ ["members", "update", "--id", memberId, "--data-json", payload("member", { first_name: "CLI" })],
220
+ ["teams", "create", "--data-json", payload("team", { name: `CLI Verify Team ${Date.now()}` })],
221
+ ["teams", "update", "--id", teamId, "--data-json", payload("team", { name: `CLI Verify Team Update ${Date.now()}` })],
222
+ ["organizations", "update", "--id", orgId, "--data-json", payload("organization", { workspace_name: "Wave Verify" })],
223
+ ["organizations", "meta-profile", "update", "--id", orgId, "--data-json", payload("organization_meta_profile", { title: "Verify Title" })],
224
+ ["organizations", "key-metric-meta-profile", "update", "--id", orgId, "--data-json", payload("key_metric_meta_profile", { annual_revenue: "1000" })],
225
+ ["issues", "create", "--issue-group-id", issueGroupId, "--name", "CLI Verify Issue", "--issue-type", "personal"],
226
+ ["issues", "update", "--id", issueId, "--data-json", payload("issue", { description: "verify-update" })],
227
+ ["lists", "create", "--data-json", payload("list", { name: "CLI Verify List", status: "in_progress", priority: "no_priority" })],
228
+ ["lists", "update", "--id", listId, "--data-json", payload("list", { description: "verify-update" })],
229
+ ["list-items", "create", "--data-json", payload("list_item", { list_id: listId, summary: "CLI Verify List Item" })],
230
+ ["list-items", "update", "--id", listItemId, "--data-json", payload("list_item", { summary: "CLI Verify List Item Update" })],
231
+ ["issue-groups", "create", "--data-json", payload("issue_group", { name: "CLI Verify Issue Group", status: "in_progress", priority: "no_priority" })],
232
+ ["issue-groups", "update", "--id", issueGroupId, "--data-json", payload("issue_group", { description: "verify-update" })],
233
+ ["todo-groups", "create", "--data-json", payload("todo_group", { name: "CLI Verify Todo Group", status: "in_progress", priority: "no_priority" })],
234
+ ["todo-groups", "update", "--id", todoGroupId, "--data-json", payload("todo_group", { description: "verify-update" })],
235
+ ["todos", "create", "--data-json", payload("todo", { todo_group_id: todoGroupId, name: "CLI Verify Todo", status: "to_do", priority: "no_priority" })],
236
+ ["todos", "update", "--id", todoId, "--data-json", payload("todo", { description: "verify-update" })],
237
+ ["rock-collections", "create", "--data-json", payload("rock_collection", { name: "CLI Verify Rock Collection", status: "in_progress", priority: "no_priority" })],
238
+ ["rock-collections", "update", "--id", rockCollectionId, "--data-json", payload("rock_collection", { description: "verify-update" })],
239
+ ["knowledge", "create", "--data-json", payload("content", { type: "normal", content_type: "knowledge", status: "draft", name: "CLI Verify Doc" })],
240
+ ["knowledge", "update", "--id", contentId, "--data-json", payload("content", { name: "CLI Verify Doc Update" })],
241
+ ["stand-ups", "create", "--data-json", payload("stand_up", { member_id: memberId, completed_date: "2026-04-06" })],
242
+ ["stand-ups", "update", "--id", standUpId, "--data-json", payload("stand_up", { blockers: "verify-update" })],
243
+ ["news", "create", "--data-json", payload("headline", { summary: "CLI Verify Headline", status: "draft", headline_type: "organizational" })],
244
+ ["news", "update", "--id", headlineId, "--data-json", payload("headline", { description: "verify-update" })],
245
+ ["questions", "create", "--data-json", payload("question", { summary: "CLI Verify Question" })],
246
+ ["questions", "update", "--id", questionId, "--data-json", payload("question", { summary: "CLI Verify Question Update" })],
247
+ ["pulse", "create", "--data-json", payload("health_update", { updatable_id: projectId, updatable_type: "Project", value: "on_track", status: "on_track" })],
248
+ ["pulse", "update", "--id", pulseId, "--data-json", payload("health_update", { value: "verify-update" })],
249
+ ["surveys", "create", "--data-json", payload("survey", { title: "CLI Verify Survey" })],
250
+ ["surveys", "update", "--id", surveyId, "--data-json", payload("survey", { title: "CLI Verify Survey Update" })],
251
+ ["feedbacks", "create", "--data-json", payload("feedback", { title: "CLI Verify Feedback" })],
252
+ ["feedbacks", "update", "--id", feedbackId, "--data-json", payload("feedback", { title: "CLI Verify Feedback Update" })],
253
+ ["accountability", "create", "--data-json", payload("responsibility", { summary: "CLI Verify Responsibility" })],
254
+ ["accountability", "update", "--id", responsibilityId, "--data-json", payload("responsibility", { summary: "CLI Verify Responsibility Update" })],
255
+ ["kpis", "create", "--data-json", payload("smart_kpi", { measurable_group_id: measurableGroupId, name: "CLI Verify KPI" })],
256
+ ["kpis", "update", "--id", kpiId, "--data-json", payload("smart_kpi", { name: "CLI Verify KPI Update" })],
257
+ ["scorecard-groups", "create", "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group" })],
258
+ ["scorecard-groups", "update", "--id", measurableGroupId, "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group Update" })],
259
+ ["scorecards", "create", "--data-json", payload("measurable", { measurable_group_id: measurableGroupId, name: "CLI Verify Scorecard" })],
260
+ ["scorecards", "update", "--id", measurableId, "--data-json", payload("measurable", { name: "CLI Verify Scorecard Update" })],
261
+ ["customers", "create", "--data-json", payload("customer", { name: "CLI Verify Customer" })],
262
+ ["customers", "update", "--id", customerId, "--data-json", payload("customer", { name: "CLI Verify Customer Update" })],
263
+ ["contacts", "create", "--data-json", payload("contact", { customer_id: customerId, name: "CLI Verify Contact" })],
264
+ ["contacts", "update", "--id", contactId, "--data-json", payload("contact", { name: "CLI Verify Contact Update" })],
265
+ ["foundation", "strategic-plans", "update", "--id", orgId, "--data-json", payload("strategic_plan", { title: "CLI Verify Strategic Plan" })],
266
+ ["foundation", "strategic-objectives", "update", "--id", orgId, "--data-json", payload("strategic_objective", { title: "CLI Verify Strategic Objective" })],
267
+ ["foundation", "annual-objectives", "create", "--data-json", payload("annual_objective", { strategic_objective_id: annualObjectiveId || orgId, title: "CLI Verify Annual Objective" })],
268
+ ["foundation", "annual-objectives", "update", "--id", annualObjectiveId, "--data-json", payload("annual_objective", { title: "CLI Verify Annual Objective Update" })],
269
+ ["foundation", "quarterly-objectives", "create", "--data-json", payload("quarterly_objective", { strategic_objective_id: annualObjectiveId || orgId, annual_objective_id: annualObjectiveId || orgId, title: "CLI Verify Quarterly Objective" })],
270
+ ["foundation", "quarterly-objectives", "update", "--id", quarterlyObjectiveId, "--data-json", payload("quarterly_objective", { title: "CLI Verify Quarterly Objective Update" })]
271
+ ];
272
+
273
+ for (const args of probes) {
274
+ if (args.includes(undefined)) {
275
+ results.push({
276
+ args,
277
+ code: 0,
278
+ stdout: JSON.stringify({
279
+ ok: false,
280
+ error: { code: "skipped_missing_id", details: {} }
281
+ }),
282
+ parsed: { ok: false, error: { code: "skipped_missing_id", details: {} } }
283
+ });
284
+ continue;
285
+ }
286
+ results.push(runWave(args, env));
287
+ }
288
+
289
+ const lines = [];
290
+ let failures = 0;
291
+ for (const res of results) {
292
+ const verdict = classify(res);
293
+ const cmd = res.args.join(" ");
294
+ const status = res.parsed?.status ?? "n/a";
295
+ const errCode = res.parsed?.error?.code ?? "none";
296
+ lines.push(`${verdict.ok ? "PASS" : "FAIL"} | status=${status} | err=${errCode} | ${cmd}`);
297
+ if (!verdict.ok) failures += 1;
298
+ }
299
+
300
+ console.log(lines.join("\n"));
301
+ console.log(`\nSUMMARY total=${results.length} failures=${failures}`);
302
+ if (failures > 0) process.exit(1);