@overaihq/cli 0.4.0 → 0.6.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.
- package/README.md +1 -1
- package/dist/index.js +282 -67
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ overai clone <workspaceId> # materialize resources and WORKSPACE.md
|
|
|
24
24
|
# ...edit files...
|
|
25
25
|
overai status # local edits + remote drift
|
|
26
26
|
overai diff # what changed
|
|
27
|
-
overai review #
|
|
27
|
+
overai review # local checks + authenticated TypeScript lint + guides
|
|
28
28
|
overai report bug "<details>" # report broken or unexplained behavior
|
|
29
29
|
overai report feature "<details>" # request a missing platform capability
|
|
30
30
|
overai push -m "why" # create new versions (not live yet)
|
package/dist/index.js
CHANGED
|
@@ -26690,26 +26690,6 @@ var readValue = async (value, label) => {
|
|
|
26690
26690
|
}
|
|
26691
26691
|
};
|
|
26692
26692
|
|
|
26693
|
-
// src/gql.ts
|
|
26694
|
-
var gqlRequest = async (query, variables, options) => {
|
|
26695
|
-
const res = await v2Fetch(
|
|
26696
|
-
options?.v1 === true ? "/v1/graphql" : "/v2/graphql",
|
|
26697
|
-
{
|
|
26698
|
-
method: "POST",
|
|
26699
|
-
headers: { "Content-Type": "application/json" },
|
|
26700
|
-
body: JSON.stringify({ query, variables })
|
|
26701
|
-
}
|
|
26702
|
-
);
|
|
26703
|
-
const body = await res.json().catch(() => null);
|
|
26704
|
-
if (body?.errors != null && body.errors.length > 0) {
|
|
26705
|
-
throw new Error(body.errors.map((e) => e.message).join("; "));
|
|
26706
|
-
}
|
|
26707
|
-
if (!res.ok || body?.data == null) {
|
|
26708
|
-
throw new Error(`graphql failed (${String(res.status)})`);
|
|
26709
|
-
}
|
|
26710
|
-
return body.data;
|
|
26711
|
-
};
|
|
26712
|
-
|
|
26713
26693
|
// src/output.ts
|
|
26714
26694
|
var resolveFormat = (explicit) => {
|
|
26715
26695
|
if (explicit === "json" || explicit === "csv" || explicit === "table") {
|
|
@@ -26764,6 +26744,43 @@ var printResult = (data, format2) => {
|
|
|
26764
26744
|
`);
|
|
26765
26745
|
};
|
|
26766
26746
|
|
|
26747
|
+
// src/commands/agent.ts
|
|
26748
|
+
var runAgent = async (args) => {
|
|
26749
|
+
const [operation, id] = args;
|
|
26750
|
+
if (operation !== "generate" || id == null) throw new Error("Usage: overai agent generate <id> --message <text|@file|@->");
|
|
26751
|
+
const message = await readValue(flagValue(args, "--message", "-m"), "--message");
|
|
26752
|
+
if (message == null) throw new Error("Agent generate requires --message.");
|
|
26753
|
+
const { organizationId } = await getAccessToken();
|
|
26754
|
+
const response = await v2Fetch(`/v2/agents/${organizationId}/${id}/generate/text`, {
|
|
26755
|
+
method: "POST",
|
|
26756
|
+
headers: { "Content-Type": "application/json" },
|
|
26757
|
+
body: JSON.stringify({ message })
|
|
26758
|
+
});
|
|
26759
|
+
const body = await response.json().catch(() => null);
|
|
26760
|
+
if (!response.ok) throw new Error(body == null ? `Agent generate failed (${String(response.status)}).` : JSON.stringify(body));
|
|
26761
|
+
printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
|
|
26762
|
+
};
|
|
26763
|
+
|
|
26764
|
+
// src/gql.ts
|
|
26765
|
+
var gqlRequest = async (query, variables, options) => {
|
|
26766
|
+
const res = await v2Fetch(
|
|
26767
|
+
options?.v1 === true ? "/v1/graphql" : "/v2/graphql",
|
|
26768
|
+
{
|
|
26769
|
+
method: "POST",
|
|
26770
|
+
headers: { "Content-Type": "application/json" },
|
|
26771
|
+
body: JSON.stringify({ query, variables })
|
|
26772
|
+
}
|
|
26773
|
+
);
|
|
26774
|
+
const body = await res.json().catch(() => null);
|
|
26775
|
+
if (body?.errors != null && body.errors.length > 0) {
|
|
26776
|
+
throw new Error(body.errors.map((e) => e.message).join("; "));
|
|
26777
|
+
}
|
|
26778
|
+
if (!res.ok || body?.data == null) {
|
|
26779
|
+
throw new Error(`graphql failed (${String(res.status)})`);
|
|
26780
|
+
}
|
|
26781
|
+
return body.data;
|
|
26782
|
+
};
|
|
26783
|
+
|
|
26767
26784
|
// src/commands/automation.ts
|
|
26768
26785
|
var runRuns = async (automationId, args) => {
|
|
26769
26786
|
const limit2 = Number(flagValue(args, "--limit") ?? 20);
|
|
@@ -26854,23 +26871,6 @@ var runAutomation = async (args) => {
|
|
|
26854
26871
|
process.exitCode = 1;
|
|
26855
26872
|
};
|
|
26856
26873
|
|
|
26857
|
-
// src/commands/agent.ts
|
|
26858
|
-
var runAgent = async (args) => {
|
|
26859
|
-
const [operation, id] = args;
|
|
26860
|
-
if (operation !== "generate" || id == null) throw new Error("Usage: overai agent generate <id> --message <text|@file|@->");
|
|
26861
|
-
const message = await readValue(flagValue(args, "--message", "-m"), "--message");
|
|
26862
|
-
if (message == null) throw new Error("Agent generate requires --message.");
|
|
26863
|
-
const { organizationId } = await getAccessToken();
|
|
26864
|
-
const response = await v2Fetch(`/v2/agents/${organizationId}/${id}/generate/text`, {
|
|
26865
|
-
method: "POST",
|
|
26866
|
-
headers: { "Content-Type": "application/json" },
|
|
26867
|
-
body: JSON.stringify({ message })
|
|
26868
|
-
});
|
|
26869
|
-
const body = await response.json().catch(() => null);
|
|
26870
|
-
if (!response.ok) throw new Error(body == null ? `Agent generate failed (${String(response.status)}).` : JSON.stringify(body));
|
|
26871
|
-
printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
|
|
26872
|
-
};
|
|
26873
|
-
|
|
26874
26874
|
// src/workspace/manifest.ts
|
|
26875
26875
|
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
26876
26876
|
import { dirname, join as join2, resolve } from "path";
|
|
@@ -33428,6 +33428,92 @@ import {
|
|
|
33428
33428
|
writeFileSync as writeFileSync4
|
|
33429
33429
|
} from "fs";
|
|
33430
33430
|
import { join as join4, resolve as resolve3 } from "path";
|
|
33431
|
+
|
|
33432
|
+
// ../common/src/lint/schema.ts
|
|
33433
|
+
var lintSurfaceSchema = external_exports.enum([
|
|
33434
|
+
"webPage",
|
|
33435
|
+
// React/TSX page — gets rules-of-hooks
|
|
33436
|
+
"automationCode",
|
|
33437
|
+
// automation TS-code node (globals STATE/env/OUTPUT)
|
|
33438
|
+
"function"
|
|
33439
|
+
// Web-standard-runtime TS module (export default handler)
|
|
33440
|
+
]);
|
|
33441
|
+
var lintDiagnosticSchema = external_exports.object({
|
|
33442
|
+
line: external_exports.number(),
|
|
33443
|
+
column: external_exports.number(),
|
|
33444
|
+
/** ESLint rule id, or null for a fatal parse error. */
|
|
33445
|
+
ruleId: external_exports.string().nullable(),
|
|
33446
|
+
message: external_exports.string(),
|
|
33447
|
+
severity: external_exports.enum(["error", "warning"])
|
|
33448
|
+
});
|
|
33449
|
+
var lintRequestSchema = external_exports.object({
|
|
33450
|
+
code: external_exports.string(),
|
|
33451
|
+
surface: lintSurfaceSchema,
|
|
33452
|
+
// The workspace the code is being authored in. Used to (a) authorize the
|
|
33453
|
+
// caller as a builder in that workspace and (b) resolve its real resources so
|
|
33454
|
+
// env id references are type-narrowed. A user token carries no workspace, so
|
|
33455
|
+
// this rides the request body.
|
|
33456
|
+
workspaceId: external_exports.string(),
|
|
33457
|
+
// The linted function's own declared output schema (function surface only),
|
|
33458
|
+
// enabling the handler return-vs-schema compliance check. Client-supplied
|
|
33459
|
+
// because the draft being linted lives in the editor, unsaved; advisory, so
|
|
33460
|
+
// nothing is trusted from it. '' or omitted skips the check.
|
|
33461
|
+
functionOutputSchema: external_exports.string().optional()
|
|
33462
|
+
});
|
|
33463
|
+
var lintResponseSchema = external_exports.object({
|
|
33464
|
+
diagnostics: external_exports.array(lintDiagnosticSchema)
|
|
33465
|
+
});
|
|
33466
|
+
var functionDeployImpactRequestSchema = external_exports.object({
|
|
33467
|
+
// Authorizes the builder-in-workspace gate, like the lint request.
|
|
33468
|
+
workspaceId: external_exports.string(),
|
|
33469
|
+
functionId: external_exports.string(),
|
|
33470
|
+
/** The saved version being deployed; its schema pair is read server-side. */
|
|
33471
|
+
versionNumber: external_exports.number().int()
|
|
33472
|
+
});
|
|
33473
|
+
var functionDeployDependentSchema = external_exports.object({
|
|
33474
|
+
kind: external_exports.enum(["webPage", "printablePage", "function", "automation"]),
|
|
33475
|
+
id: external_exports.string(),
|
|
33476
|
+
name: external_exports.string()
|
|
33477
|
+
});
|
|
33478
|
+
var functionDeployImpactResponseSchema = external_exports.object({
|
|
33479
|
+
/** True only when the contract is incompatible AND deployed callers exist. */
|
|
33480
|
+
breaking: external_exports.boolean(),
|
|
33481
|
+
/** Human-readable type mismatches, one per broken direction. */
|
|
33482
|
+
reasons: external_exports.array(external_exports.string()),
|
|
33483
|
+
/** Deployed resources whose code references this function's handle. */
|
|
33484
|
+
dependents: external_exports.array(functionDeployDependentSchema)
|
|
33485
|
+
});
|
|
33486
|
+
|
|
33487
|
+
// src/deployImpact.ts
|
|
33488
|
+
var NOT_BREAKING = {
|
|
33489
|
+
breaking: false,
|
|
33490
|
+
reasons: [],
|
|
33491
|
+
dependents: []
|
|
33492
|
+
};
|
|
33493
|
+
var fetchFunctionDeployImpact = async (workspaceId, functionId, versionNumber) => {
|
|
33494
|
+
try {
|
|
33495
|
+
const res = await v2Fetch("/v2/architect/functionDeployImpact", {
|
|
33496
|
+
method: "POST",
|
|
33497
|
+
headers: { "Content-Type": "application/json" },
|
|
33498
|
+
body: JSON.stringify({ workspaceId, functionId, versionNumber })
|
|
33499
|
+
});
|
|
33500
|
+
if (!res.ok) return NOT_BREAKING;
|
|
33501
|
+
const parsed = functionDeployImpactResponseSchema.safeParse(
|
|
33502
|
+
await res.json()
|
|
33503
|
+
);
|
|
33504
|
+
return parsed.success ? parsed.data : NOT_BREAKING;
|
|
33505
|
+
} catch {
|
|
33506
|
+
return NOT_BREAKING;
|
|
33507
|
+
}
|
|
33508
|
+
};
|
|
33509
|
+
var breakingDeployMessage = (name2, versionNumber, impact) => [
|
|
33510
|
+
`SKIP ${name2}: v${String(versionNumber)}'s declared input/output schemas are incompatible with deployed callers:`,
|
|
33511
|
+
...impact.dependents.map((d) => ` ${d.name} (${d.kind}, ${d.id})`),
|
|
33512
|
+
...impact.reasons.map((r) => ` ${r}`),
|
|
33513
|
+
`Fix the callers (or the schemas) first, or pass --allow-breaking to deploy anyway.`
|
|
33514
|
+
].join("\n");
|
|
33515
|
+
|
|
33516
|
+
// src/commands/sync.ts
|
|
33431
33517
|
var toManifestResource = (type, remote) => ({
|
|
33432
33518
|
type,
|
|
33433
33519
|
name: remote.name,
|
|
@@ -33665,6 +33751,7 @@ var runPush = async (args) => {
|
|
|
33665
33751
|
const message = flagValue(args, "-m", "--message") ?? "Pushed via overai CLI";
|
|
33666
33752
|
const deploy = args.includes("--deploy");
|
|
33667
33753
|
const force = args.includes("--force");
|
|
33754
|
+
const allowBreaking = args.includes("--allow-breaking");
|
|
33668
33755
|
const pathFilter = positionals(args)[0];
|
|
33669
33756
|
let pushed = 0;
|
|
33670
33757
|
for (const [id, resource] of resourcesMatching(manifest, pathFilter)) {
|
|
@@ -33709,6 +33796,20 @@ var runPush = async (args) => {
|
|
|
33709
33796
|
`
|
|
33710
33797
|
);
|
|
33711
33798
|
if (deploy) {
|
|
33799
|
+
if (resource.type === "function" && !allowBreaking) {
|
|
33800
|
+
const impact = await fetchFunctionDeployImpact(
|
|
33801
|
+
manifest.workspaceId,
|
|
33802
|
+
id,
|
|
33803
|
+
versionNumber
|
|
33804
|
+
);
|
|
33805
|
+
if (impact.breaking) {
|
|
33806
|
+
process.stdout.write(
|
|
33807
|
+
`${breakingDeployMessage(resource.name, versionNumber, impact)}
|
|
33808
|
+
`
|
|
33809
|
+
);
|
|
33810
|
+
continue;
|
|
33811
|
+
}
|
|
33812
|
+
}
|
|
33712
33813
|
await adapter.deploy(id, versionNumber);
|
|
33713
33814
|
resource.deployedVersion = versionNumber;
|
|
33714
33815
|
writeManifest(root, manifest);
|
|
@@ -33726,6 +33827,7 @@ var runPush = async (args) => {
|
|
|
33726
33827
|
var runDeploy = async (args) => {
|
|
33727
33828
|
const { root, manifest } = openClone();
|
|
33728
33829
|
const pathFilter = positionals(args)[0];
|
|
33830
|
+
const allowBreaking = args.includes("--allow-breaking");
|
|
33729
33831
|
const pending = resourcesMatching(manifest, pathFilter).filter(
|
|
33730
33832
|
([, r]) => r.deployedVersion !== r.baseVersion
|
|
33731
33833
|
);
|
|
@@ -33749,6 +33851,20 @@ Pass a filter to deploy one, or --all to deploy everything.`
|
|
|
33749
33851
|
);
|
|
33750
33852
|
continue;
|
|
33751
33853
|
}
|
|
33854
|
+
if (resource.type === "function" && !allowBreaking) {
|
|
33855
|
+
const impact = await fetchFunctionDeployImpact(
|
|
33856
|
+
manifest.workspaceId,
|
|
33857
|
+
id,
|
|
33858
|
+
resource.baseVersion
|
|
33859
|
+
);
|
|
33860
|
+
if (impact.breaking) {
|
|
33861
|
+
process.stdout.write(
|
|
33862
|
+
`${breakingDeployMessage(resource.name, resource.baseVersion, impact)}
|
|
33863
|
+
`
|
|
33864
|
+
);
|
|
33865
|
+
continue;
|
|
33866
|
+
}
|
|
33867
|
+
}
|
|
33752
33868
|
await adapterFor(resource.type).deploy(id, resource.baseVersion);
|
|
33753
33869
|
resource.deployedVersion = resource.baseVersion;
|
|
33754
33870
|
writeManifest(root, manifest);
|
|
@@ -34146,30 +34262,6 @@ var runOpen = async (args) => {
|
|
|
34146
34262
|
openBrowser(url);
|
|
34147
34263
|
};
|
|
34148
34264
|
|
|
34149
|
-
// src/commands/printablePage.ts
|
|
34150
|
-
var runPrintablePage = async (args) => {
|
|
34151
|
-
const [operation, id] = args;
|
|
34152
|
-
if (operation !== "render" || id == null) throw new Error("Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]");
|
|
34153
|
-
const data = await gqlRequest(
|
|
34154
|
-
`query CliPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
|
|
34155
|
-
{ id }
|
|
34156
|
-
);
|
|
34157
|
-
if (data.printablePage == null) throw new Error(`Printable page ${id} not found.`);
|
|
34158
|
-
const searchParams = await readValue(flagValue(args, "--search-params"), "--search-params") ?? "{}";
|
|
34159
|
-
JSON.parse(searchParams);
|
|
34160
|
-
const { organizationId } = await getAccessToken();
|
|
34161
|
-
const timeoutMs = Number(flagValue(args, "--timeout") ?? 6e4);
|
|
34162
|
-
const response = await v2Fetch(`/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`, {
|
|
34163
|
-
method: "POST",
|
|
34164
|
-
headers: { "Content-Type": "application/json" },
|
|
34165
|
-
body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
|
|
34166
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
34167
|
-
});
|
|
34168
|
-
const body = await response.json().catch(() => null);
|
|
34169
|
-
if (!response.ok) throw new Error(JSON.stringify(body));
|
|
34170
|
-
printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
|
|
34171
|
-
};
|
|
34172
|
-
|
|
34173
34265
|
// src/commands/org.ts
|
|
34174
34266
|
var USER_ORGS_QUERY = `
|
|
34175
34267
|
query UserOrganizations($identityId: String!) {
|
|
@@ -34235,6 +34327,30 @@ ${result.redirectUrl}
|
|
|
34235
34327
|
`);
|
|
34236
34328
|
};
|
|
34237
34329
|
|
|
34330
|
+
// src/commands/printablePage.ts
|
|
34331
|
+
var runPrintablePage = async (args) => {
|
|
34332
|
+
const [operation, id] = args;
|
|
34333
|
+
if (operation !== "render" || id == null) throw new Error("Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]");
|
|
34334
|
+
const data = await gqlRequest(
|
|
34335
|
+
`query CliPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
|
|
34336
|
+
{ id }
|
|
34337
|
+
);
|
|
34338
|
+
if (data.printablePage == null) throw new Error(`Printable page ${id} not found.`);
|
|
34339
|
+
const searchParams = await readValue(flagValue(args, "--search-params"), "--search-params") ?? "{}";
|
|
34340
|
+
JSON.parse(searchParams);
|
|
34341
|
+
const { organizationId } = await getAccessToken();
|
|
34342
|
+
const timeoutMs = Number(flagValue(args, "--timeout") ?? 6e4);
|
|
34343
|
+
const response = await v2Fetch(`/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`, {
|
|
34344
|
+
method: "POST",
|
|
34345
|
+
headers: { "Content-Type": "application/json" },
|
|
34346
|
+
body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
|
|
34347
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
34348
|
+
});
|
|
34349
|
+
const body = await response.json().catch(() => null);
|
|
34350
|
+
if (!response.ok) throw new Error(JSON.stringify(body));
|
|
34351
|
+
printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
|
|
34352
|
+
};
|
|
34353
|
+
|
|
34238
34354
|
// src/commands/update.ts
|
|
34239
34355
|
import { spawnSync } from "child_process";
|
|
34240
34356
|
import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
|
|
@@ -34629,6 +34745,10 @@ executionMemory MB).
|
|
|
34629
34745
|
responds with \u2014 no request envelope) so env.FNxxx.call() is typed for
|
|
34630
34746
|
callers: the deployed version's schemas become FunctionHandle<In, Out> in
|
|
34631
34747
|
the editor and lint. Leave it "" when the response shape is unspecified.
|
|
34748
|
+
- That contract is checked at deploy time: \`overai deploy\` and
|
|
34749
|
+
\`overai push --deploy\` skip a function whose declared schema change is
|
|
34750
|
+
incompatible with deployed callers, printing the affected resources. Fix the
|
|
34751
|
+
callers (or the schemas) first, or pass \`--allow-breaking\` to deploy anyway.
|
|
34632
34752
|
- Verify with \`overai fn <id> --input k=v\` (invokes the DEPLOYED version;
|
|
34633
34753
|
add \`--method\`/\`--path\` to exercise one route of a router) and
|
|
34634
34754
|
\`overai fn logs <id>\` / \`--execution <FEX id>\` for status, timing, logs,
|
|
@@ -34644,7 +34764,8 @@ var workflow = `# CLI editing workflow
|
|
|
34644
34764
|
each nested workspace you need separately.
|
|
34645
34765
|
2. Edit files. The id in each filename is authoritative; slugs are cosmetic.
|
|
34646
34766
|
3. \`overai status\` / \`overai diff\` \u2014 review what changed.
|
|
34647
|
-
4. \`overai review\` \u2014
|
|
34767
|
+
4. \`overai review\` \u2014 local checks, authenticated TypeScript lint against the
|
|
34768
|
+
live workspace, and these guides; fix findings first.
|
|
34648
34769
|
5. \`overai push -m "why"\` \u2014 creates NEW versions (not live). Fast-forward
|
|
34649
34770
|
only: if the resource moved remotely, pull, re-apply, retry. Write commit
|
|
34650
34771
|
messages like PR titles \u2014 they're the version history.
|
|
@@ -34682,11 +34803,11 @@ var runBestPractices = (args) => {
|
|
|
34682
34803
|
);
|
|
34683
34804
|
};
|
|
34684
34805
|
var runReview = async () => {
|
|
34685
|
-
const root =
|
|
34686
|
-
const manifest = readManifest(root);
|
|
34806
|
+
const { root, manifest } = openClone();
|
|
34687
34807
|
const findings2 = [];
|
|
34688
34808
|
const touchedTypes = /* @__PURE__ */ new Set();
|
|
34689
34809
|
const changed = [];
|
|
34810
|
+
const lintTargets = [];
|
|
34690
34811
|
for (const [id, resource] of Object.entries(manifest.resources)) {
|
|
34691
34812
|
const { state, changedFiles } = localState(root, resource);
|
|
34692
34813
|
if (state !== "modified") continue;
|
|
@@ -34733,6 +34854,37 @@ var runReview = async () => {
|
|
|
34733
34854
|
}
|
|
34734
34855
|
}
|
|
34735
34856
|
}
|
|
34857
|
+
if (resource.type === "webPage") {
|
|
34858
|
+
changedFiles.filter((relPath) => relPath.endsWith(".tsx")).forEach((relPath) => {
|
|
34859
|
+
const code = readWorkingFile(root, relPath);
|
|
34860
|
+
if (code != null) {
|
|
34861
|
+
lintTargets.push({ label: relPath, code, surface: "webPage" });
|
|
34862
|
+
}
|
|
34863
|
+
});
|
|
34864
|
+
}
|
|
34865
|
+
if (resource.type === "function") {
|
|
34866
|
+
const codeFile = resource.files.find((path) => path.endsWith(".ts"));
|
|
34867
|
+
const metaFile = resource.files.find(
|
|
34868
|
+
(path) => path.endsWith(".meta.json")
|
|
34869
|
+
);
|
|
34870
|
+
const code = codeFile == null ? null : readWorkingFile(root, codeFile);
|
|
34871
|
+
const meta = metaFile == null ? null : readWorkingFile(root, metaFile);
|
|
34872
|
+
if (codeFile != null && code != null) {
|
|
34873
|
+
const parsedMeta = (() => {
|
|
34874
|
+
try {
|
|
34875
|
+
return JSON.parse(meta ?? "null");
|
|
34876
|
+
} catch {
|
|
34877
|
+
return null;
|
|
34878
|
+
}
|
|
34879
|
+
})();
|
|
34880
|
+
lintTargets.push({
|
|
34881
|
+
label: codeFile,
|
|
34882
|
+
code,
|
|
34883
|
+
surface: "function",
|
|
34884
|
+
functionOutputSchema: parsedMeta != null && typeof parsedMeta === "object" && "outputSchema" in parsedMeta && typeof parsedMeta.outputSchema === "string" ? parsedMeta.outputSchema : void 0
|
|
34885
|
+
});
|
|
34886
|
+
}
|
|
34887
|
+
}
|
|
34736
34888
|
if (resource.type === "automation") {
|
|
34737
34889
|
const docFile = resource.files.find((f) => f.endsWith(".json"));
|
|
34738
34890
|
const raw2 = docFile == null ? null : readWorkingFile(root, docFile);
|
|
@@ -34744,6 +34896,25 @@ var runReview = async () => {
|
|
|
34744
34896
|
(f) => `${docFile} elements.${f}`
|
|
34745
34897
|
)
|
|
34746
34898
|
);
|
|
34899
|
+
const nodes = Array.isArray(doc.elements) ? [...doc.elements] : [];
|
|
34900
|
+
for (const node of nodes) {
|
|
34901
|
+
if (node == null || typeof node !== "object") continue;
|
|
34902
|
+
const nodeRecord = node;
|
|
34903
|
+
if (Array.isArray(nodeRecord.elements)) {
|
|
34904
|
+
nodes.push(...nodeRecord.elements);
|
|
34905
|
+
}
|
|
34906
|
+
const inputs = nodeRecord.inputs;
|
|
34907
|
+
if (nodeRecord.nodeTypeId !== "TYPESCRIPT_CODE" || inputs == null || typeof inputs !== "object") {
|
|
34908
|
+
continue;
|
|
34909
|
+
}
|
|
34910
|
+
const code = inputs.code;
|
|
34911
|
+
if (typeof code !== "string") continue;
|
|
34912
|
+
lintTargets.push({
|
|
34913
|
+
label: `${docFile} ${typeof nodeRecord.variableId === "string" ? nodeRecord.variableId : "unknown node"}`,
|
|
34914
|
+
code,
|
|
34915
|
+
surface: "automationCode"
|
|
34916
|
+
});
|
|
34917
|
+
}
|
|
34747
34918
|
} catch {
|
|
34748
34919
|
}
|
|
34749
34920
|
}
|
|
@@ -34753,6 +34924,47 @@ var runReview = async () => {
|
|
|
34753
34924
|
process.stdout.write("Nothing modified locally \u2014 nothing to review.\n");
|
|
34754
34925
|
return;
|
|
34755
34926
|
}
|
|
34927
|
+
for (const target of lintTargets) {
|
|
34928
|
+
try {
|
|
34929
|
+
const response = await v2Fetch("/v2/architect/lint", {
|
|
34930
|
+
method: "POST",
|
|
34931
|
+
headers: { "Content-Type": "application/json" },
|
|
34932
|
+
body: JSON.stringify({
|
|
34933
|
+
code: target.code,
|
|
34934
|
+
surface: target.surface,
|
|
34935
|
+
workspaceId: manifest.workspaceId,
|
|
34936
|
+
functionOutputSchema: target.functionOutputSchema
|
|
34937
|
+
})
|
|
34938
|
+
});
|
|
34939
|
+
const responseText = await response.text();
|
|
34940
|
+
if (!response.ok) {
|
|
34941
|
+
throw new Error(
|
|
34942
|
+
`${target.label}: HTTP ${String(response.status)}${responseText ? ` \u2014 ${responseText}` : ""}`
|
|
34943
|
+
);
|
|
34944
|
+
}
|
|
34945
|
+
const parsed = lintResponseSchema.safeParse(
|
|
34946
|
+
(() => {
|
|
34947
|
+
try {
|
|
34948
|
+
return JSON.parse(responseText);
|
|
34949
|
+
} catch {
|
|
34950
|
+
return null;
|
|
34951
|
+
}
|
|
34952
|
+
})()
|
|
34953
|
+
);
|
|
34954
|
+
if (!parsed.success) {
|
|
34955
|
+
throw new Error(`${target.label}: invalid response from lint service`);
|
|
34956
|
+
}
|
|
34957
|
+
findings2.push(
|
|
34958
|
+
...parsed.data.diagnostics.map(
|
|
34959
|
+
(diagnostic) => `${target.label}:${String(diagnostic.line)}:${String(diagnostic.column)}: ${diagnostic.severity}: ${diagnostic.message}${diagnostic.ruleId == null ? "" : ` (${diagnostic.ruleId})`}`
|
|
34960
|
+
)
|
|
34961
|
+
);
|
|
34962
|
+
} catch (error) {
|
|
34963
|
+
findings2.push(
|
|
34964
|
+
`${target.label}: remote lint could not run \u2014 ${error instanceof Error ? error.message : String(error)}`
|
|
34965
|
+
);
|
|
34966
|
+
}
|
|
34967
|
+
}
|
|
34756
34968
|
const guides = ["workflow", ...touchedTypes].map((t) => t === "webPage" || t === "printablePage" ? "pages" : t).map((t) => t === "automation" ? "automations" : t).map((t) => t === "function" ? "functions" : t);
|
|
34757
34969
|
const guideDocs = [...new Set(guides)].map((t) => BEST_PRACTICES[t]).filter((d) => d != null);
|
|
34758
34970
|
process.stdout.write(
|
|
@@ -35410,14 +35622,17 @@ Workspace sync (edit resources as local files):
|
|
|
35410
35622
|
overai outline <filter>[@ref] Top-level declarations with their line
|
|
35411
35623
|
ranges and doc comments
|
|
35412
35624
|
overai pull Fast-forward clean resources to remote
|
|
35413
|
-
overai push [filter] -m <msg> [--deploy] [--force]
|
|
35625
|
+
overai push [filter] -m <msg> [--deploy] [--force] [--allow-breaking]
|
|
35414
35626
|
Create new versions from local edits
|
|
35415
|
-
overai deploy [filter | --all] Point deployed versions at pushed ones
|
|
35627
|
+
overai deploy [filter | --all] Point deployed versions at pushed ones;
|
|
35628
|
+
a function whose schema change breaks
|
|
35629
|
+
deployed callers is skipped unless
|
|
35630
|
+
--allow-breaking
|
|
35416
35631
|
overai restore <filter | --all> Discard local edits (back to base)
|
|
35417
35632
|
overai open [filter | id] Open a resource (or the workspace) in the webapp
|
|
35418
35633
|
overai dev <filter> [--port N] [--open]
|
|
35419
35634
|
Live-preview a web page from local files
|
|
35420
|
-
overai review
|
|
35635
|
+
overai review Local checks + authenticated TypeScript lint + guides
|
|
35421
35636
|
overai best-practices [topic] Print bundled guides
|
|
35422
35637
|
overai report bug|feature "<details>"
|
|
35423
35638
|
Send product feedback (use - for stdin)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@overaihq/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Create, edit, deploy, and run OverAI workspace resources from the command line",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://overai.com",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"lint": "echo 'Run lint from the root of the repo'",
|
|
31
31
|
"ts-check": "tsc --noEmit",
|
|
32
32
|
"build": "tsup",
|
|
33
|
-
"build:watch": "
|
|
33
|
+
"build:watch": "tsup --watch",
|
|
34
34
|
"start": "node dist/index.js",
|
|
35
35
|
"test": "vitest",
|
|
36
36
|
"test:localhost": "vitest --run --config vitest.localhost.config.js"
|