create-settlegrid-tool 1.0.0 → 1.0.1
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/index.js +308 -25
- package/dist/postinstall.js +114 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import prompts from "prompts";
|
|
5
5
|
import pc3 from "picocolors";
|
|
6
|
-
import
|
|
6
|
+
import path4 from "path";
|
|
7
7
|
|
|
8
8
|
// src/scaffold.ts
|
|
9
9
|
import fs from "fs-extra";
|
|
@@ -128,7 +128,7 @@ startCommand = "npm start"
|
|
|
128
128
|
|
|
129
129
|
// src/banner.ts
|
|
130
130
|
import pc2 from "picocolors";
|
|
131
|
-
function banner() {
|
|
131
|
+
function banner(version) {
|
|
132
132
|
const emerald = pc2.green;
|
|
133
133
|
const dim = pc2.dim;
|
|
134
134
|
const art = `
|
|
@@ -138,14 +138,209 @@ ${emerald(" \\__ \\/ -_) _| _| / -_)| (_ | '_| / _` |")}
|
|
|
138
138
|
${emerald(" |___/\\___|\\__|\\__|_\\___| \\___|_| |_\\__,_|")}
|
|
139
139
|
`;
|
|
140
140
|
const tagline = dim(" The Settlement Layer for the AI Economy");
|
|
141
|
-
const
|
|
141
|
+
const versionLine = dim(` v${version}`);
|
|
142
142
|
const separator = dim(" " + "-".repeat(44));
|
|
143
143
|
return `${art}${tagline}
|
|
144
|
-
${
|
|
144
|
+
${versionLine}
|
|
145
145
|
${separator}
|
|
146
146
|
`;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
// src/args.ts
|
|
150
|
+
var TEMPLATE_EQ_PREFIX = "--template=";
|
|
151
|
+
function parseArgs(argv) {
|
|
152
|
+
const result = { help: false, version: false };
|
|
153
|
+
for (let i = 0; i < argv.length; i++) {
|
|
154
|
+
const arg = argv[i];
|
|
155
|
+
if (arg === "--help" || arg === "-h") {
|
|
156
|
+
result.help = true;
|
|
157
|
+
} else if (arg === "--version") {
|
|
158
|
+
result.version = true;
|
|
159
|
+
} else if (arg === "--template") {
|
|
160
|
+
const next = argv[i + 1];
|
|
161
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
162
|
+
result.template = next;
|
|
163
|
+
i++;
|
|
164
|
+
} else {
|
|
165
|
+
result.template = "";
|
|
166
|
+
}
|
|
167
|
+
} else if (arg.startsWith(TEMPLATE_EQ_PREFIX)) {
|
|
168
|
+
result.template = arg.slice(TEMPLATE_EQ_PREFIX.length);
|
|
169
|
+
} else if (arg.startsWith("-")) {
|
|
170
|
+
} else if (result.directory === void 0) {
|
|
171
|
+
result.directory = arg;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/version.ts
|
|
178
|
+
import { readFileSync } from "fs";
|
|
179
|
+
import * as path2 from "path";
|
|
180
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
181
|
+
function readPackageVersion() {
|
|
182
|
+
try {
|
|
183
|
+
const here = path2.dirname(fileURLToPath2(import.meta.url));
|
|
184
|
+
const pkgPath = path2.resolve(here, "..", "package.json");
|
|
185
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
186
|
+
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
|
|
187
|
+
} catch {
|
|
188
|
+
return "unknown";
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/gallery.ts
|
|
193
|
+
import fs2 from "fs-extra";
|
|
194
|
+
var GALLERY_OWNER = "settlegrid";
|
|
195
|
+
var GALLERY_REPO_PREFIX = "settlegrid-";
|
|
196
|
+
var MAX_SLUG_LEN = 64;
|
|
197
|
+
var GALLERY_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
198
|
+
var ScaffoldError = class extends Error {
|
|
199
|
+
code;
|
|
200
|
+
constructor(code, message) {
|
|
201
|
+
super(message);
|
|
202
|
+
this.name = "ScaffoldError";
|
|
203
|
+
this.code = code;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
function isValidGallerySlug(slug) {
|
|
207
|
+
return slug.length > 0 && slug.length <= MAX_SLUG_LEN && GALLERY_SLUG_RE.test(slug);
|
|
208
|
+
}
|
|
209
|
+
function gallerySource(slug) {
|
|
210
|
+
return `github:${GALLERY_OWNER}/${GALLERY_REPO_PREFIX}${slug}`;
|
|
211
|
+
}
|
|
212
|
+
function defaultGalleryDir(slug) {
|
|
213
|
+
return `${GALLERY_REPO_PREFIX}${slug}`;
|
|
214
|
+
}
|
|
215
|
+
function classifyGigetError(err) {
|
|
216
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
217
|
+
if (msg.includes("404") || msg.includes("not found")) {
|
|
218
|
+
return "template_not_found";
|
|
219
|
+
}
|
|
220
|
+
if (msg.includes("eacces") || msg.includes("eperm") || msg.includes("erofs") || msg.includes("enospc") || msg.includes("eexist") || msg.includes("permission denied")) {
|
|
221
|
+
return "write_failed";
|
|
222
|
+
}
|
|
223
|
+
return "download_failed";
|
|
224
|
+
}
|
|
225
|
+
async function downloadGalleryTemplate(slug, targetDir) {
|
|
226
|
+
if (!isValidGallerySlug(slug)) {
|
|
227
|
+
throw new ScaffoldError(
|
|
228
|
+
"template_not_found",
|
|
229
|
+
slug.trim().length === 0 ? "Missing template slug. Usage: npx create-settlegrid-tool --template <slug>" : `Unknown or invalid template "${slug}". Slugs are lowercase letters, digits, and hyphens.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (await fs2.pathExists(targetDir)) {
|
|
233
|
+
const entries = await fs2.readdir(targetDir);
|
|
234
|
+
if (entries.length > 0) {
|
|
235
|
+
throw new ScaffoldError(
|
|
236
|
+
"write_failed",
|
|
237
|
+
`Directory "${targetDir}" already exists and is not empty.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const { downloadTemplate } = await import("giget");
|
|
243
|
+
await downloadTemplate(gallerySource(slug), {
|
|
244
|
+
dir: targetDir,
|
|
245
|
+
force: true
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
throw new ScaffoldError(
|
|
249
|
+
classifyGigetError(err),
|
|
250
|
+
err instanceof Error ? err.message : String(err)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/telemetry.ts
|
|
256
|
+
import { chmodSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
257
|
+
import { homedir } from "os";
|
|
258
|
+
import * as path3 from "path";
|
|
259
|
+
import { randomUUID } from "crypto";
|
|
260
|
+
var DEFAULT_PROXY_BASE = "https://settlegrid.ai";
|
|
261
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
262
|
+
function isOptedOut() {
|
|
263
|
+
if (isCi()) return true;
|
|
264
|
+
const raw = process.env.SETTLEGRID_TELEMETRY?.trim().toLowerCase();
|
|
265
|
+
if (!raw) return false;
|
|
266
|
+
return raw === "0" || raw === "false" || raw === "no" || raw === "off";
|
|
267
|
+
}
|
|
268
|
+
function isCi() {
|
|
269
|
+
return process.env.CI !== void 0 || process.env.CONTINUOUS_INTEGRATION !== void 0;
|
|
270
|
+
}
|
|
271
|
+
function getProxyBase() {
|
|
272
|
+
const raw = process.env.SETTLEGRID_API_URL?.trim();
|
|
273
|
+
const base = raw && raw.length > 0 ? raw : DEFAULT_PROXY_BASE;
|
|
274
|
+
return base.replace(/\/$/, "");
|
|
275
|
+
}
|
|
276
|
+
function telemetryIdPath() {
|
|
277
|
+
const home = homedir() || "/tmp";
|
|
278
|
+
return path3.join(home, ".settlegrid", "telemetry-id");
|
|
279
|
+
}
|
|
280
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
281
|
+
function getDistinctId() {
|
|
282
|
+
const envId = process.env.SETTLEGRID_POSTHOG_ID?.trim();
|
|
283
|
+
if (envId && UUID_RE.test(envId)) return envId;
|
|
284
|
+
const file = telemetryIdPath();
|
|
285
|
+
try {
|
|
286
|
+
const existing = readFileSync2(file, "utf8").trim();
|
|
287
|
+
if (UUID_RE.test(existing)) return existing;
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
const id = randomUUID();
|
|
291
|
+
try {
|
|
292
|
+
mkdirSync(path3.dirname(file), { recursive: true, mode: 448 });
|
|
293
|
+
writeFileSync(file, id, { mode: 384 });
|
|
294
|
+
chmodSync(file, 384);
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
return id;
|
|
298
|
+
}
|
|
299
|
+
var fetchOverride;
|
|
300
|
+
async function post(body) {
|
|
301
|
+
const fetchImpl = fetchOverride ?? globalThis.fetch;
|
|
302
|
+
if (typeof fetchImpl !== "function") {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const url = `${getProxyBase()}/api/telemetry/capture`;
|
|
306
|
+
const controller = new AbortController();
|
|
307
|
+
const timer = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
308
|
+
try {
|
|
309
|
+
const res = await fetchImpl(url, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: { "content-type": "application/json" },
|
|
312
|
+
body: JSON.stringify(body),
|
|
313
|
+
redirect: "error",
|
|
314
|
+
signal: controller.signal
|
|
315
|
+
});
|
|
316
|
+
return res.ok;
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
} finally {
|
|
320
|
+
clearTimeout(timer);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async function capture(event, properties) {
|
|
324
|
+
if (isOptedOut()) return false;
|
|
325
|
+
return post({
|
|
326
|
+
event,
|
|
327
|
+
properties,
|
|
328
|
+
distinct_id: getDistinctId()
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async function captureScaffoldSuccess(args) {
|
|
332
|
+
return capture("scaffold_success", {
|
|
333
|
+
template_slug: args.template_slug,
|
|
334
|
+
duration_ms: args.duration_ms
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
async function captureScaffoldFailed(args) {
|
|
338
|
+
return capture("scaffold_failed", {
|
|
339
|
+
template_slug: args.template_slug,
|
|
340
|
+
error_code: args.error_code
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
149
344
|
// src/index.ts
|
|
150
345
|
var CATEGORIES = [
|
|
151
346
|
{ title: "Data", value: "data" },
|
|
@@ -177,31 +372,85 @@ var DEPLOY_TARGETS = [
|
|
|
177
372
|
function toSlug(name) {
|
|
178
373
|
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
179
374
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
375
|
+
function helpText() {
|
|
376
|
+
return `
|
|
377
|
+
${pc3.bold("Usage:")}
|
|
378
|
+
${pc3.cyan("npx create-settlegrid-tool")} ${pc3.dim("[directory]")}
|
|
379
|
+
${pc3.cyan("npx create-settlegrid-tool --template")} ${pc3.dim("<slug> [directory]")}
|
|
380
|
+
|
|
381
|
+
${pc3.dim("Create a monetized AI tool project with SettleGrid billing.")}
|
|
186
382
|
|
|
187
|
-
${pc3.
|
|
383
|
+
${pc3.bold("Modes:")}
|
|
384
|
+
${pc3.dim("Interactive")} Run with no ${pc3.cyan("--template")} flag to pick a starter
|
|
385
|
+
archetype (blank, rest-api, openapi, mcp-server).
|
|
386
|
+
${pc3.dim("Gallery")} Pass ${pc3.cyan("--template <slug>")} to scaffold one of the
|
|
387
|
+
published templates from ${pc3.underline("https://settlegrid.ai/templates")}.
|
|
188
388
|
|
|
189
389
|
${pc3.bold("Options:")}
|
|
190
|
-
${pc3.cyan("
|
|
191
|
-
${pc3.cyan("
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
390
|
+
${pc3.cyan("--template <slug>")} Gallery template slug, e.g. ${pc3.dim("tmdb")} (non-interactive)
|
|
391
|
+
${pc3.cyan("[directory]")} Target directory (default: ${pc3.dim("settlegrid-<slug>")} in
|
|
392
|
+
gallery mode, or prompted in interactive mode)
|
|
393
|
+
${pc3.cyan("--help, -h")} Show this help message
|
|
394
|
+
${pc3.cyan("--version")} Show version
|
|
395
|
+
`;
|
|
396
|
+
}
|
|
397
|
+
function mapInteractiveError(err) {
|
|
398
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
399
|
+
if (msg.includes("already exists")) return "write_failed";
|
|
400
|
+
if (msg.includes("not found")) return "template_not_found";
|
|
401
|
+
return "unknown";
|
|
402
|
+
}
|
|
403
|
+
async function runGalleryMode(slug, directoryArg) {
|
|
404
|
+
const scaffoldStart = Date.now();
|
|
405
|
+
const trimmedSlug = slug.trim();
|
|
406
|
+
const telemetrySlug = isValidGallerySlug(trimmedSlug) ? trimmedSlug : "(invalid)";
|
|
407
|
+
const directory = directoryArg ?? defaultGalleryDir(trimmedSlug);
|
|
408
|
+
const targetDir = path4.resolve(process.cwd(), directory);
|
|
409
|
+
console.log(
|
|
410
|
+
pc3.dim(` Scaffolding "${trimmedSlug}" from the SettleGrid gallery...`)
|
|
411
|
+
);
|
|
412
|
+
console.log();
|
|
413
|
+
try {
|
|
414
|
+
await downloadGalleryTemplate(trimmedSlug, targetDir);
|
|
415
|
+
} catch (err) {
|
|
416
|
+
const code = err instanceof ScaffoldError ? err.code : "unknown";
|
|
417
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
418
|
+
console.error(pc3.red(" Scaffold failed: ") + message);
|
|
419
|
+
if (code === "template_not_found") {
|
|
420
|
+
console.error(
|
|
421
|
+
pc3.dim(" Browse available templates at ") + pc3.cyan("https://settlegrid.ai/templates")
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
console.error();
|
|
425
|
+
process.exitCode = 1;
|
|
426
|
+
void captureScaffoldFailed({
|
|
427
|
+
template_slug: telemetrySlug,
|
|
428
|
+
error_code: code
|
|
429
|
+
});
|
|
430
|
+
return;
|
|
199
431
|
}
|
|
432
|
+
console.log(pc3.green(pc3.bold(" Done!")) + " Template ready.\n");
|
|
433
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan(`cd ${directory}`)}`);
|
|
434
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan("npm install")}`);
|
|
435
|
+
console.log(
|
|
436
|
+
` ${pc3.dim("$")} ${pc3.cyan("cp .env.example .env")} ${pc3.dim("# add your API keys")}`
|
|
437
|
+
);
|
|
438
|
+
console.log(` ${pc3.dim("$")} ${pc3.cyan("npm run dev")}
|
|
439
|
+
`);
|
|
440
|
+
console.log(
|
|
441
|
+
pc3.dim(" Register your tool to start earning: ") + pc3.cyan(pc3.underline("https://settlegrid.ai/dashboard/tools")) + "\n"
|
|
442
|
+
);
|
|
443
|
+
void captureScaffoldSuccess({
|
|
444
|
+
template_slug: telemetrySlug,
|
|
445
|
+
duration_ms: Date.now() - scaffoldStart
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
async function runInteractiveMode(directoryArg) {
|
|
200
449
|
let cancelled = false;
|
|
201
450
|
const response = await prompts(
|
|
202
451
|
[
|
|
203
452
|
{
|
|
204
|
-
type:
|
|
453
|
+
type: directoryArg ? null : "text",
|
|
205
454
|
name: "directory",
|
|
206
455
|
message: "Project directory",
|
|
207
456
|
initial: "my-tool",
|
|
@@ -211,7 +460,7 @@ async function main() {
|
|
|
211
460
|
type: "text",
|
|
212
461
|
name: "toolName",
|
|
213
462
|
message: "Tool name",
|
|
214
|
-
initial: (prev) => prev ||
|
|
463
|
+
initial: (prev) => prev || directoryArg || "my-tool"
|
|
215
464
|
},
|
|
216
465
|
{
|
|
217
466
|
type: "text",
|
|
@@ -261,9 +510,9 @@ async function main() {
|
|
|
261
510
|
console.log(pc3.red("\nSetup cancelled."));
|
|
262
511
|
process.exit(1);
|
|
263
512
|
}
|
|
264
|
-
const directory =
|
|
513
|
+
const directory = directoryArg || response.directory;
|
|
265
514
|
const toolSlug = toSlug(directory);
|
|
266
|
-
const targetDir =
|
|
515
|
+
const targetDir = path4.resolve(process.cwd(), directory);
|
|
267
516
|
const config = {
|
|
268
517
|
directory,
|
|
269
518
|
toolName: response.toolName || directory,
|
|
@@ -279,7 +528,19 @@ async function main() {
|
|
|
279
528
|
console.log();
|
|
280
529
|
console.log(pc3.dim(" Scaffolding project..."));
|
|
281
530
|
console.log();
|
|
282
|
-
|
|
531
|
+
const scaffoldStart = Date.now();
|
|
532
|
+
try {
|
|
533
|
+
await scaffold(config);
|
|
534
|
+
} catch (err) {
|
|
535
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
536
|
+
console.error(pc3.red(" Scaffold failed: ") + message + "\n");
|
|
537
|
+
process.exitCode = 1;
|
|
538
|
+
void captureScaffoldFailed({
|
|
539
|
+
template_slug: config.template,
|
|
540
|
+
error_code: mapInteractiveError(err)
|
|
541
|
+
});
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
283
544
|
console.log(pc3.green(pc3.bold(" Done!")) + " Your tool is ready.\n");
|
|
284
545
|
console.log(` ${pc3.dim("$")} ${pc3.cyan(`cd ${directory}`)}`);
|
|
285
546
|
console.log(` ${pc3.dim("$")} ${pc3.cyan("npm install")}`);
|
|
@@ -288,6 +549,28 @@ async function main() {
|
|
|
288
549
|
console.log(
|
|
289
550
|
pc3.dim(" Next: Register your tool at ") + pc3.cyan(pc3.underline("https://settlegrid.ai/dashboard/tools")) + "\n"
|
|
290
551
|
);
|
|
552
|
+
void captureScaffoldSuccess({
|
|
553
|
+
template_slug: config.template,
|
|
554
|
+
duration_ms: Date.now() - scaffoldStart
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async function main() {
|
|
558
|
+
const version = readPackageVersion();
|
|
559
|
+
const args = parseArgs(process.argv.slice(2));
|
|
560
|
+
console.log(banner(version));
|
|
561
|
+
if (args.help) {
|
|
562
|
+
console.log(helpText());
|
|
563
|
+
process.exit(0);
|
|
564
|
+
}
|
|
565
|
+
if (args.version) {
|
|
566
|
+
console.log(version);
|
|
567
|
+
process.exit(0);
|
|
568
|
+
}
|
|
569
|
+
if (args.template !== void 0) {
|
|
570
|
+
await runGalleryMode(args.template, args.directory);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
await runInteractiveMode(args.directory);
|
|
291
574
|
}
|
|
292
575
|
main().catch((err) => {
|
|
293
576
|
console.error(pc3.red("Error:"), err instanceof Error ? err.message : err);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/telemetry.ts
|
|
4
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
var DEFAULT_PROXY_BASE = "https://settlegrid.ai";
|
|
9
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
10
|
+
function isOptedOut() {
|
|
11
|
+
if (isCi()) return true;
|
|
12
|
+
const raw = process.env.SETTLEGRID_TELEMETRY?.trim().toLowerCase();
|
|
13
|
+
if (!raw) return false;
|
|
14
|
+
return raw === "0" || raw === "false" || raw === "no" || raw === "off";
|
|
15
|
+
}
|
|
16
|
+
function isCi() {
|
|
17
|
+
return process.env.CI !== void 0 || process.env.CONTINUOUS_INTEGRATION !== void 0;
|
|
18
|
+
}
|
|
19
|
+
function getProxyBase() {
|
|
20
|
+
const raw = process.env.SETTLEGRID_API_URL?.trim();
|
|
21
|
+
const base = raw && raw.length > 0 ? raw : DEFAULT_PROXY_BASE;
|
|
22
|
+
return base.replace(/\/$/, "");
|
|
23
|
+
}
|
|
24
|
+
function telemetryIdPath() {
|
|
25
|
+
const home = homedir() || "/tmp";
|
|
26
|
+
return path.join(home, ".settlegrid", "telemetry-id");
|
|
27
|
+
}
|
|
28
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29
|
+
function getDistinctId() {
|
|
30
|
+
const envId = process.env.SETTLEGRID_POSTHOG_ID?.trim();
|
|
31
|
+
if (envId && UUID_RE.test(envId)) return envId;
|
|
32
|
+
const file = telemetryIdPath();
|
|
33
|
+
try {
|
|
34
|
+
const existing = readFileSync(file, "utf8").trim();
|
|
35
|
+
if (UUID_RE.test(existing)) return existing;
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
const id = randomUUID();
|
|
39
|
+
try {
|
|
40
|
+
mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
|
|
41
|
+
writeFileSync(file, id, { mode: 384 });
|
|
42
|
+
chmodSync(file, 384);
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
return id;
|
|
46
|
+
}
|
|
47
|
+
var fetchOverride;
|
|
48
|
+
async function post(body) {
|
|
49
|
+
const fetchImpl = fetchOverride ?? globalThis.fetch;
|
|
50
|
+
if (typeof fetchImpl !== "function") {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const url = `${getProxyBase()}/api/telemetry/capture`;
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const timer = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetchImpl(url, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "content-type": "application/json" },
|
|
60
|
+
body: JSON.stringify(body),
|
|
61
|
+
redirect: "error",
|
|
62
|
+
signal: controller.signal
|
|
63
|
+
});
|
|
64
|
+
return res.ok;
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
} finally {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function capture(event, properties) {
|
|
72
|
+
if (isOptedOut()) return false;
|
|
73
|
+
return post({
|
|
74
|
+
event,
|
|
75
|
+
properties,
|
|
76
|
+
distinct_id: getDistinctId()
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function captureCliInstallStarted(args) {
|
|
80
|
+
return capture("cli_install_started", {
|
|
81
|
+
cli_version: args.cli_version,
|
|
82
|
+
node_version: process.versions.node,
|
|
83
|
+
os: `${process.platform}-${process.arch}`
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/version.ts
|
|
88
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
89
|
+
import * as path2 from "path";
|
|
90
|
+
import { fileURLToPath } from "url";
|
|
91
|
+
function readPackageVersion() {
|
|
92
|
+
try {
|
|
93
|
+
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
94
|
+
const pkgPath = path2.resolve(here, "..", "package.json");
|
|
95
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
96
|
+
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "unknown";
|
|
97
|
+
} catch {
|
|
98
|
+
return "unknown";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/postinstall.ts
|
|
103
|
+
var SAFETY_EXIT_MS = 2500;
|
|
104
|
+
(async () => {
|
|
105
|
+
const safety = setTimeout(() => process.exit(0), SAFETY_EXIT_MS);
|
|
106
|
+
safety.unref();
|
|
107
|
+
try {
|
|
108
|
+
await captureCliInstallStarted({ cli_version: readPackageVersion() });
|
|
109
|
+
} catch {
|
|
110
|
+
} finally {
|
|
111
|
+
clearTimeout(safety);
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-settlegrid-tool",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Create a monetized AI tool with SettleGrid in 30 seconds",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"settlegrid",
|
|
@@ -47,10 +47,12 @@
|
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"test:watch": "vitest",
|
|
49
49
|
"lint": "tsc --noEmit",
|
|
50
|
+
"postinstall": "node ./dist/postinstall.js || true",
|
|
50
51
|
"prepublishOnly": "npm run build"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"fs-extra": "^11.2.0",
|
|
55
|
+
"giget": "^1.2.3",
|
|
54
56
|
"picocolors": "^1.1.0",
|
|
55
57
|
"prompts": "^2.4.2"
|
|
56
58
|
},
|