@dashai/cli 0.3.2 → 0.4.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/dist/bin.js +252 -44
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2032,6 +2032,47 @@ var init_init = __esm({
|
|
|
2032
2032
|
init_workspaces();
|
|
2033
2033
|
}
|
|
2034
2034
|
});
|
|
2035
|
+
function loadSchemaCommandContext(opts) {
|
|
2036
|
+
const projectRoot = resolve(opts.dir ?? process.cwd());
|
|
2037
|
+
const manifestPath2 = join(projectRoot, "module.json");
|
|
2038
|
+
const manifest = readManifestOrThrow(projectRoot);
|
|
2039
|
+
const manifestRawBytes = readFileSync(manifestPath2, "utf-8");
|
|
2040
|
+
const slug = manifest.module?.slug;
|
|
2041
|
+
if (!slug) {
|
|
2042
|
+
throw new Error(
|
|
2043
|
+
`module.json at ${manifestPath2} has no module.slug \u2014 the manifest is malformed.`
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
const cfg = readConfig();
|
|
2047
|
+
if (!cfg) {
|
|
2048
|
+
throw new Error("Not logged in. Run `dashwise login` first.");
|
|
2049
|
+
}
|
|
2050
|
+
const entry = readModuleEntry(slug);
|
|
2051
|
+
if (!entry) {
|
|
2052
|
+
throw new Error(
|
|
2053
|
+
`No dev install cached for module "${slug}". Run \`dashwise init ${slug} --force\` to provision one.`
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
return {
|
|
2057
|
+
projectRoot,
|
|
2058
|
+
manifestPath: manifestPath2,
|
|
2059
|
+
manifest,
|
|
2060
|
+
manifestRawBytes,
|
|
2061
|
+
slug,
|
|
2062
|
+
cfg,
|
|
2063
|
+
entry,
|
|
2064
|
+
requestOptions: {
|
|
2065
|
+
baseUrl: resolveApiUrl(),
|
|
2066
|
+
auth: { token: entry.runtimeToken }
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
var init_schema_command_context = __esm({
|
|
2071
|
+
"src/lib/schema-command-context.ts"() {
|
|
2072
|
+
init_config();
|
|
2073
|
+
init_manifest_io();
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2035
2076
|
|
|
2036
2077
|
// src/commands/module/destroy-dev-install.ts
|
|
2037
2078
|
var destroy_dev_install_exports = {};
|
|
@@ -2202,47 +2243,6 @@ var init_reset_dev_install = __esm({
|
|
|
2202
2243
|
init_output();
|
|
2203
2244
|
}
|
|
2204
2245
|
});
|
|
2205
|
-
function loadSchemaCommandContext(opts) {
|
|
2206
|
-
const projectRoot = resolve(opts.dir ?? process.cwd());
|
|
2207
|
-
const manifestPath2 = join(projectRoot, "module.json");
|
|
2208
|
-
const manifest = readManifestOrThrow(projectRoot);
|
|
2209
|
-
const manifestRawBytes = readFileSync(manifestPath2, "utf-8");
|
|
2210
|
-
const slug = manifest.module?.slug;
|
|
2211
|
-
if (!slug) {
|
|
2212
|
-
throw new Error(
|
|
2213
|
-
`module.json at ${manifestPath2} has no module.slug \u2014 the manifest is malformed.`
|
|
2214
|
-
);
|
|
2215
|
-
}
|
|
2216
|
-
const cfg = readConfig();
|
|
2217
|
-
if (!cfg) {
|
|
2218
|
-
throw new Error("Not logged in. Run `dashwise login` first.");
|
|
2219
|
-
}
|
|
2220
|
-
const entry = readModuleEntry(slug);
|
|
2221
|
-
if (!entry) {
|
|
2222
|
-
throw new Error(
|
|
2223
|
-
`No dev install cached for module "${slug}". Run \`dashwise init ${slug} --force\` to provision one.`
|
|
2224
|
-
);
|
|
2225
|
-
}
|
|
2226
|
-
return {
|
|
2227
|
-
projectRoot,
|
|
2228
|
-
manifestPath: manifestPath2,
|
|
2229
|
-
manifest,
|
|
2230
|
-
manifestRawBytes,
|
|
2231
|
-
slug,
|
|
2232
|
-
cfg,
|
|
2233
|
-
entry,
|
|
2234
|
-
requestOptions: {
|
|
2235
|
-
baseUrl: resolveApiUrl(),
|
|
2236
|
-
auth: { token: entry.runtimeToken }
|
|
2237
|
-
}
|
|
2238
|
-
};
|
|
2239
|
-
}
|
|
2240
|
-
var init_schema_command_context = __esm({
|
|
2241
|
-
"src/lib/schema-command-context.ts"() {
|
|
2242
|
-
init_config();
|
|
2243
|
-
init_manifest_io();
|
|
2244
|
-
}
|
|
2245
|
-
});
|
|
2246
2246
|
|
|
2247
2247
|
// src/lib/field-spec.ts
|
|
2248
2248
|
function parseFieldSpec(spec) {
|
|
@@ -2707,10 +2707,76 @@ var init_set_type = __esm({
|
|
|
2707
2707
|
init_api_client();
|
|
2708
2708
|
init_config();
|
|
2709
2709
|
init_manifest_io();
|
|
2710
|
+
|
|
2711
|
+
// src/lib/manifest-poll.ts
|
|
2712
|
+
init_api_client();
|
|
2713
|
+
function startManifestPollLoop(opts) {
|
|
2714
|
+
const {
|
|
2715
|
+
installationId,
|
|
2716
|
+
manifestPath: manifestPath2,
|
|
2717
|
+
requestOptions,
|
|
2718
|
+
intervalMs,
|
|
2719
|
+
onUpdate,
|
|
2720
|
+
onError
|
|
2721
|
+
} = opts;
|
|
2722
|
+
let lastSeenJson = existsSync(manifestPath2) ? readFileSync(manifestPath2, "utf-8") : "";
|
|
2723
|
+
let timer = null;
|
|
2724
|
+
let stopped = false;
|
|
2725
|
+
const tick = async () => {
|
|
2726
|
+
if (stopped) return;
|
|
2727
|
+
try {
|
|
2728
|
+
const { manifest } = await getInstallationSchema(
|
|
2729
|
+
installationId,
|
|
2730
|
+
requestOptions
|
|
2731
|
+
);
|
|
2732
|
+
const remoteJson = JSON.stringify(manifest, null, 2) + "\n";
|
|
2733
|
+
const currentLocal = existsSync(manifestPath2) ? readFileSync(manifestPath2, "utf-8") : "";
|
|
2734
|
+
if (remoteJson === currentLocal) {
|
|
2735
|
+
lastSeenJson = remoteJson;
|
|
2736
|
+
} else if (remoteJson !== lastSeenJson) {
|
|
2737
|
+
writeFileSync(manifestPath2, remoteJson, "utf-8");
|
|
2738
|
+
lastSeenJson = remoteJson;
|
|
2739
|
+
if (onUpdate) {
|
|
2740
|
+
const delta = Buffer.byteLength(remoteJson, "utf8") - Buffer.byteLength(currentLocal, "utf8");
|
|
2741
|
+
const deltaStr = delta === 0 ? "0 bytes" : `${delta > 0 ? "+" : ""}${delta} bytes`;
|
|
2742
|
+
onUpdate(
|
|
2743
|
+
`Backend manifest changed \u2014 synced to module.json (${deltaStr}).`
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
} catch (err) {
|
|
2748
|
+
if (onError) {
|
|
2749
|
+
const message = err instanceof ApiError ? `${err.code} (HTTP ${err.status}): ${err.message}` : err.message;
|
|
2750
|
+
onError(new Error(message));
|
|
2751
|
+
}
|
|
2752
|
+
} finally {
|
|
2753
|
+
if (!stopped) {
|
|
2754
|
+
timer = setTimeout(() => void tick(), intervalMs);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
timer = setTimeout(() => void tick(), intervalMs);
|
|
2759
|
+
return {
|
|
2760
|
+
stop: () => {
|
|
2761
|
+
if (stopped) return;
|
|
2762
|
+
stopped = true;
|
|
2763
|
+
if (timer) {
|
|
2764
|
+
clearTimeout(timer);
|
|
2765
|
+
timer = null;
|
|
2766
|
+
}
|
|
2767
|
+
},
|
|
2768
|
+
isRunning: () => !stopped
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
// src/commands/dev.ts
|
|
2710
2773
|
init_output();
|
|
2711
2774
|
var DEFAULT_NEXT_PORT = 3e3;
|
|
2712
2775
|
var PREFIX_SDK = `\x1B[36m[sdk]\x1B[0m `;
|
|
2713
2776
|
var PREFIX_NEXT = `\x1B[35m[next]\x1B[0m `;
|
|
2777
|
+
var PREFIX_POLL = `\x1B[33m[poll]\x1B[0m `;
|
|
2778
|
+
var DEFAULT_REMOTE_POLL_INTERVAL_SEC = 5;
|
|
2779
|
+
var MIN_REMOTE_POLL_INTERVAL_SEC = 1;
|
|
2714
2780
|
async function devCommand(options) {
|
|
2715
2781
|
if (options.sdkOnly && options.nextOnly) {
|
|
2716
2782
|
fail(
|
|
@@ -2845,10 +2911,43 @@ async function devCommand(options) {
|
|
|
2845
2911
|
pipeWithPrefix(child.stdout, process.stdout, prefix);
|
|
2846
2912
|
pipeWithPrefix(child.stderr, process.stderr, prefix);
|
|
2847
2913
|
}
|
|
2914
|
+
let manifestPoll = null;
|
|
2915
|
+
const enablePoll = !options.noRemotePoll && moduleEntry !== void 0 && manifestSlug !== void 0;
|
|
2916
|
+
if (enablePoll) {
|
|
2917
|
+
const intervalSec = Math.max(
|
|
2918
|
+
MIN_REMOTE_POLL_INTERVAL_SEC,
|
|
2919
|
+
options.remotePollIntervalSec ?? DEFAULT_REMOTE_POLL_INTERVAL_SEC
|
|
2920
|
+
);
|
|
2921
|
+
manifestPoll = startManifestPollLoop({
|
|
2922
|
+
installationId: moduleEntry.installationId,
|
|
2923
|
+
manifestPath: manifestPath2,
|
|
2924
|
+
requestOptions: {
|
|
2925
|
+
baseUrl: apiUrl,
|
|
2926
|
+
auth: { token: moduleEntry.runtimeToken }
|
|
2927
|
+
},
|
|
2928
|
+
intervalMs: intervalSec * 1e3,
|
|
2929
|
+
onUpdate: (msg) => {
|
|
2930
|
+
process.stdout.write(`${PREFIX_POLL}${msg}
|
|
2931
|
+
`);
|
|
2932
|
+
},
|
|
2933
|
+
onError: (err) => {
|
|
2934
|
+
process.stderr.write(`${PREFIX_POLL}${err.message}
|
|
2935
|
+
`);
|
|
2936
|
+
}
|
|
2937
|
+
});
|
|
2938
|
+
info(
|
|
2939
|
+
` ${dim("poll")} backend manifest every ${intervalSec}s ${dim("(--no-remote-poll to disable)")}`
|
|
2940
|
+
);
|
|
2941
|
+
info("");
|
|
2942
|
+
}
|
|
2848
2943
|
let shuttingDown = false;
|
|
2849
2944
|
const forwardSignal = (sig) => {
|
|
2850
2945
|
if (shuttingDown) return;
|
|
2851
2946
|
shuttingDown = true;
|
|
2947
|
+
if (manifestPoll) {
|
|
2948
|
+
manifestPoll.stop();
|
|
2949
|
+
manifestPoll = null;
|
|
2950
|
+
}
|
|
2852
2951
|
info(`
|
|
2853
2952
|
Received ${sig}, stopping children\u2026`);
|
|
2854
2953
|
for (const { child } of children) {
|
|
@@ -2885,6 +2984,10 @@ Received ${sig}, stopping children\u2026`);
|
|
|
2885
2984
|
}
|
|
2886
2985
|
}
|
|
2887
2986
|
}
|
|
2987
|
+
if (manifestPoll) {
|
|
2988
|
+
manifestPoll.stop();
|
|
2989
|
+
manifestPoll = null;
|
|
2990
|
+
}
|
|
2888
2991
|
const exitCode = Math.max(0, ...results.map((r) => r.code));
|
|
2889
2992
|
if (exitCode === 0) {
|
|
2890
2993
|
success("Dev loop stopped cleanly.");
|
|
@@ -5030,6 +5133,92 @@ async function depsRemoveCommand(providerSlug, options) {
|
|
|
5030
5133
|
return 0;
|
|
5031
5134
|
}
|
|
5032
5135
|
|
|
5136
|
+
// src/commands/pull.ts
|
|
5137
|
+
init_api_client();
|
|
5138
|
+
init_manifest_io();
|
|
5139
|
+
init_schema_command_context();
|
|
5140
|
+
init_output();
|
|
5141
|
+
async function pullCommand(options) {
|
|
5142
|
+
let ctx;
|
|
5143
|
+
try {
|
|
5144
|
+
ctx = loadSchemaCommandContext({
|
|
5145
|
+
...options.dir !== void 0 ? { dir: options.dir } : {}
|
|
5146
|
+
});
|
|
5147
|
+
} catch (err) {
|
|
5148
|
+
fail(err.message);
|
|
5149
|
+
return 1;
|
|
5150
|
+
}
|
|
5151
|
+
info(`Pulling manifest for installation ${ctx.entry.installationId}\u2026`);
|
|
5152
|
+
let remoteManifest;
|
|
5153
|
+
try {
|
|
5154
|
+
const response = await getInstallationSchema(
|
|
5155
|
+
ctx.entry.installationId,
|
|
5156
|
+
ctx.requestOptions
|
|
5157
|
+
);
|
|
5158
|
+
remoteManifest = response.manifest;
|
|
5159
|
+
} catch (err) {
|
|
5160
|
+
return surfacePullFailure(err);
|
|
5161
|
+
}
|
|
5162
|
+
const localJson = JSON.stringify(ctx.manifest, null, 2) + "\n";
|
|
5163
|
+
const remoteJson = JSON.stringify(remoteManifest, null, 2) + "\n";
|
|
5164
|
+
if (localJson === remoteJson) {
|
|
5165
|
+
success("Already in sync \u2014 local module.json matches backend.");
|
|
5166
|
+
return 0;
|
|
5167
|
+
}
|
|
5168
|
+
const localBytes = Buffer.byteLength(localJson, "utf8");
|
|
5169
|
+
const remoteBytes = Buffer.byteLength(remoteJson, "utf8");
|
|
5170
|
+
const delta = remoteBytes - localBytes;
|
|
5171
|
+
const deltaStr = delta === 0 ? "0 bytes" : `${delta > 0 ? "+" : ""}${delta} bytes`;
|
|
5172
|
+
if (options.dryRun) {
|
|
5173
|
+
info(
|
|
5174
|
+
`Drift detected (${deltaStr}). ${code("--dry-run")} \u2014 not writing.`
|
|
5175
|
+
);
|
|
5176
|
+
info(`Run ${code("dashwise pull")} without ${code("--dry-run")} to apply.`);
|
|
5177
|
+
return 0;
|
|
5178
|
+
}
|
|
5179
|
+
try {
|
|
5180
|
+
writeManifest(remoteManifest, ctx.projectRoot);
|
|
5181
|
+
} catch (err) {
|
|
5182
|
+
fail(`Failed to write ${manifestPath(ctx.projectRoot)}: ${err.message}`);
|
|
5183
|
+
return 1;
|
|
5184
|
+
}
|
|
5185
|
+
success(
|
|
5186
|
+
`Synced module.json from backend (${deltaStr}). ${dim(`Run \`dashwise module generate\` to refresh typed SDK if your watcher isn't running.`)}`
|
|
5187
|
+
);
|
|
5188
|
+
return 0;
|
|
5189
|
+
}
|
|
5190
|
+
function surfacePullFailure(err) {
|
|
5191
|
+
if (err instanceof ApiError) {
|
|
5192
|
+
switch (err.code) {
|
|
5193
|
+
case "UNAUTHENTICATED":
|
|
5194
|
+
fail(
|
|
5195
|
+
`Runtime token rejected. The cached token for this module may be expired \u2014 re-run \`dashwise init <slug> --force\` to mint a fresh one.`
|
|
5196
|
+
);
|
|
5197
|
+
break;
|
|
5198
|
+
case "SANDBOX_TOKEN_NOT_ACCEPTED":
|
|
5199
|
+
fail(
|
|
5200
|
+
`Schema-read endpoint requires a local-dev runtime token (kind=local). The cached token is the wrong kind \u2014 re-run \`dashwise init <slug> --force\`.`
|
|
5201
|
+
);
|
|
5202
|
+
break;
|
|
5203
|
+
case "INSTALL_MISMATCH":
|
|
5204
|
+
fail(
|
|
5205
|
+
`Runtime token is bound to a different installation than expected. The cached install id may be stale \u2014 re-run \`dashwise init <slug> --force\`.`
|
|
5206
|
+
);
|
|
5207
|
+
break;
|
|
5208
|
+
case "INSTALLATION_NOT_FOUND":
|
|
5209
|
+
fail(
|
|
5210
|
+
`The backend has no record of this installation. It may have been destroyed (GC, manual cleanup). Re-run \`dashwise init <slug>\` to provision a fresh one.`
|
|
5211
|
+
);
|
|
5212
|
+
break;
|
|
5213
|
+
default:
|
|
5214
|
+
fail(`Backend rejected: ${err.code} ${err.status} \u2014 ${err.message}`);
|
|
5215
|
+
}
|
|
5216
|
+
return 1;
|
|
5217
|
+
}
|
|
5218
|
+
fail(`Pull failed: ${err.message}`);
|
|
5219
|
+
return 1;
|
|
5220
|
+
}
|
|
5221
|
+
|
|
5033
5222
|
// src/commands/query.ts
|
|
5034
5223
|
init_manifest_io();
|
|
5035
5224
|
init_output();
|
|
@@ -5635,7 +5824,14 @@ program.command("dev").description(
|
|
|
5635
5824
|
).option(
|
|
5636
5825
|
"--api-url <url>",
|
|
5637
5826
|
"Override the `DASHWISE_API_URL` env var passed to children (default: saved CLI config)."
|
|
5638
|
-
).option("--port <number>", "Port for `next dev` (default: 3000).", parseIntOption).option("--sdk-only", "Only run the manifest watcher; skip `next dev`.").option("--next-only", "Only run `next dev`; skip the manifest watcher.").
|
|
5827
|
+
).option("--port <number>", "Port for `next dev` (default: 3000).", parseIntOption).option("--sdk-only", "Only run the manifest watcher; skip `next dev`.").option("--next-only", "Only run `next dev`; skip the manifest watcher.").option(
|
|
5828
|
+
"--no-remote-poll",
|
|
5829
|
+
"Disable periodic backend manifest polling. By default, `dashwise dev` polls `GET /api/installations/:id/schema` every few seconds + writes module.json when the backend has advanced (dashboard edits, AI builder mutations, teammate commands)."
|
|
5830
|
+
).option(
|
|
5831
|
+
"--remote-poll-interval <seconds>",
|
|
5832
|
+
"Backend manifest poll interval, in seconds. Default: 5. Minimum: 1.",
|
|
5833
|
+
parseIntOption
|
|
5834
|
+
).action(
|
|
5639
5835
|
async (cmdOpts) => {
|
|
5640
5836
|
process.exitCode = await devCommand({
|
|
5641
5837
|
...cmdOpts.dir !== void 0 ? { dir: cmdOpts.dir } : {},
|
|
@@ -5645,7 +5841,11 @@ program.command("dev").description(
|
|
|
5645
5841
|
...cmdOpts.apiUrl !== void 0 ? { apiUrl: cmdOpts.apiUrl } : {},
|
|
5646
5842
|
...cmdOpts.port !== void 0 ? { port: cmdOpts.port } : {},
|
|
5647
5843
|
...cmdOpts.sdkOnly !== void 0 ? { sdkOnly: cmdOpts.sdkOnly } : {},
|
|
5648
|
-
...cmdOpts.nextOnly !== void 0 ? { nextOnly: cmdOpts.nextOnly } : {}
|
|
5844
|
+
...cmdOpts.nextOnly !== void 0 ? { nextOnly: cmdOpts.nextOnly } : {},
|
|
5845
|
+
// Commander's --no-foo flag maps to `foo: false`. devCommand's
|
|
5846
|
+
// option is `noRemotePoll: boolean` — invert to map cleanly.
|
|
5847
|
+
...cmdOpts.remotePoll === false ? { noRemotePoll: true } : {},
|
|
5848
|
+
...cmdOpts.remotePollInterval !== void 0 ? { remotePollIntervalSec: cmdOpts.remotePollInterval } : {}
|
|
5649
5849
|
});
|
|
5650
5850
|
}
|
|
5651
5851
|
);
|
|
@@ -5797,6 +5997,14 @@ program.command("query").description(
|
|
|
5797
5997
|
});
|
|
5798
5998
|
}
|
|
5799
5999
|
);
|
|
6000
|
+
program.command("pull").description(
|
|
6001
|
+
"Sync local module.json from the backend. Fetches the canonical manifest for the current install and writes it to disk. Inverse of `dashwise table/field` commands."
|
|
6002
|
+
).option("--dir <path>", "Project root containing `module.json` (default: current directory).").option("--dry-run", "Fetch + report drift but do not write to module.json.").action(async (cmdOpts) => {
|
|
6003
|
+
process.exitCode = await pullCommand({
|
|
6004
|
+
...cmdOpts.dir !== void 0 ? { dir: cmdOpts.dir } : {},
|
|
6005
|
+
...cmdOpts.dryRun !== void 0 ? { dryRun: cmdOpts.dryRun } : {}
|
|
6006
|
+
});
|
|
6007
|
+
});
|
|
5800
6008
|
moduleCmd.command("publish").description("Pack the current directory + upload it as a new module version.").option("--dir <path>", "Project root (default: current directory).").option("--bump <direction>", "Semver bump: patch | minor | major.").option("--dry-run", "Validate + upload but do not persist the version row.").option("-y, --yes", "Skip the confirmation prompt (for CI / scripted use).").option(
|
|
5801
6009
|
"--exclude <pattern...>",
|
|
5802
6010
|
"Extra exclude patterns (segment-matched) appended to the defaults."
|
|
@@ -5988,7 +6196,7 @@ fieldCmd.command("set-type <table-slug> <field-slug> <new-type>").description(
|
|
|
5988
6196
|
}
|
|
5989
6197
|
})();
|
|
5990
6198
|
function getVersion() {
|
|
5991
|
-
return "0.
|
|
6199
|
+
return "0.4.0";
|
|
5992
6200
|
}
|
|
5993
6201
|
function parseIntOption(value) {
|
|
5994
6202
|
const n = Number.parseInt(value, 10);
|