wizards 0.1.9
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/LICENSE +110 -0
- package/README.md +170 -0
- package/apps/runtime/dist/agents/architect.js +453 -0
- package/apps/runtime/dist/agents/project-assistant.js +354 -0
- package/apps/runtime/dist/agents/subscription.js +206 -0
- package/apps/runtime/dist/agents/thought.js +21 -0
- package/apps/runtime/dist/agents/website.js +197 -0
- package/apps/runtime/dist/app.js +153 -0
- package/apps/runtime/dist/auth/account.js +121 -0
- package/apps/runtime/dist/auth/index.js +329 -0
- package/apps/runtime/dist/auth/keys.js +55 -0
- package/apps/runtime/dist/auth/local-ticket.js +23 -0
- package/apps/runtime/dist/authoring/example.js +73 -0
- package/apps/runtime/dist/authoring/guide.js +259 -0
- package/apps/runtime/dist/authoring/ops.js +117 -0
- package/apps/runtime/dist/cli/autostart.js +134 -0
- package/apps/runtime/dist/cli/clients.js +44 -0
- package/apps/runtime/dist/cli/environment.js +81 -0
- package/apps/runtime/dist/cli/exec.js +45 -0
- package/apps/runtime/dist/cli/home.js +74 -0
- package/apps/runtime/dist/cli/machine.js +145 -0
- package/apps/runtime/dist/cli/main.js +125 -0
- package/apps/runtime/dist/cli/setup.js +273 -0
- package/apps/runtime/dist/cli/start.js +190 -0
- package/apps/runtime/dist/cli/status.js +120 -0
- package/apps/runtime/dist/cli/ui.js +12 -0
- package/apps/runtime/dist/cli/update.js +58 -0
- package/apps/runtime/dist/connectors/builtin.js +70 -0
- package/apps/runtime/dist/connectors/external.js +400 -0
- package/apps/runtime/dist/connectors/gmail.js +107 -0
- package/apps/runtime/dist/connectors/imap.js +217 -0
- package/apps/runtime/dist/connectors/index.js +266 -0
- package/apps/runtime/dist/connectors/mail.js +72 -0
- package/apps/runtime/dist/connectors/outlook.js +90 -0
- package/apps/runtime/dist/credits/credits.js +103 -0
- package/apps/runtime/dist/credits/estimate.js +132 -0
- package/apps/runtime/dist/db/adopt.js +149 -0
- package/apps/runtime/dist/db/client.js +146 -0
- package/apps/runtime/dist/db/control-schema.js +70 -0
- package/apps/runtime/dist/db/migrations/control/0000_last_absorbing_man.sql +47 -0
- package/apps/runtime/dist/db/migrations/control/0001_gorgeous_nicolaos.sql +41 -0
- package/apps/runtime/dist/db/migrations/control/0002_secret_penance.sql +1 -0
- package/apps/runtime/dist/db/migrations/control/0003_nasty_mordo.sql +3 -0
- package/apps/runtime/dist/db/migrations/control/0004_breezy_eddie_brock.sql +6 -0
- package/apps/runtime/dist/db/migrations/control/meta/0000_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0001_snapshot.json +599 -0
- package/apps/runtime/dist/db/migrations/control/meta/0002_snapshot.json +607 -0
- package/apps/runtime/dist/db/migrations/control/meta/0003_snapshot.json +311 -0
- package/apps/runtime/dist/db/migrations/control/meta/0004_snapshot.json +350 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +41 -0
- package/apps/runtime/dist/db/migrations/tenant/0000_eminent_freak.sql +188 -0
- package/apps/runtime/dist/db/migrations/tenant/0001_salty_master_chief.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0002_cloudy_firebird.sql +1 -0
- package/apps/runtime/dist/db/migrations/tenant/0003_brown_warbound.sql +52 -0
- package/apps/runtime/dist/db/migrations/tenant/0004_romantic_nocturne.sql +6 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0000_snapshot.json +1323 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0001_snapshot.json +1330 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0002_snapshot.json +1337 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0003_snapshot.json +1605 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0004_snapshot.json +1645 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +41 -0
- package/apps/runtime/dist/db/schema.js +278 -0
- package/apps/runtime/dist/documents/parse.js +230 -0
- package/apps/runtime/dist/engenty/connections-external/build-connector.js +193 -0
- package/apps/runtime/dist/engenty/connections-external/errors.js +11 -0
- package/apps/runtime/dist/engenty/connections-external/import-service.js +270 -0
- package/apps/runtime/dist/engenty/connections-external/importer/classify.js +34 -0
- package/apps/runtime/dist/engenty/connections-external/importer/map-auth.js +185 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-mcp.js +63 -0
- package/apps/runtime/dist/engenty/connections-external/importer/normalize-openapi.js +130 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/http-invoker.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/invoke/mcp-client.js +143 -0
- package/apps/runtime/dist/engenty/connections-external/net/guarded-fetch.js +119 -0
- package/apps/runtime/dist/engenty/connections-external/oauth-dcr.js +88 -0
- package/apps/runtime/dist/engenty/connections-external/registry-client.js +336 -0
- package/apps/runtime/dist/engenty/connections-external/registry-source.js +82 -0
- package/apps/runtime/dist/engenty/connections-external/types.js +7 -0
- package/apps/runtime/dist/engenty/connections-github/connector.js +209 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +359 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/contacts.js +94 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/drive.js +290 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/gmail.js +567 -0
- package/apps/runtime/dist/engenty/connections-google/definitions.js +11 -0
- package/apps/runtime/dist/engenty/connections-google/shared.js +73 -0
- package/apps/runtime/dist/engenty/connections-hubspot/connector.js +140 -0
- package/apps/runtime/dist/engenty/connections-microsoft/action.js +17 -0
- package/apps/runtime/dist/engenty/connections-microsoft/graph.js +114 -0
- package/apps/runtime/dist/engenty/connections-microsoft/onedrive.js +286 -0
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +464 -0
- package/apps/runtime/dist/engenty/connections-s3/s3.js +328 -0
- package/apps/runtime/dist/engenty/connections-sdk/files-capability.js +86 -0
- package/apps/runtime/dist/engenty/connections-sdk/oauth2.js +150 -0
- package/apps/runtime/dist/engenty/connections-sdk/registry.js +89 -0
- package/apps/runtime/dist/engenty/connections-sdk/storage-capability.js +84 -0
- package/apps/runtime/dist/engenty/connections-sdk/types.js +30 -0
- package/apps/runtime/dist/engenty/connections-slack/connector.js +360 -0
- package/apps/runtime/dist/engenty/csv-import/csv-matrix.js +178 -0
- package/apps/runtime/dist/engenty/csv-import/import-sources.js +87 -0
- package/apps/runtime/dist/engenty/csv-import/parse-csv.js +129 -0
- package/apps/runtime/dist/engenty/csv-import/serialize-csv.js +20 -0
- package/apps/runtime/dist/engenty/csv-import/types.js +1 -0
- package/apps/runtime/dist/engenty/csv-import/xlsx-workbook.js +159 -0
- package/apps/runtime/dist/engenty/doc-converter/interface.js +9 -0
- package/apps/runtime/dist/engenty/doc-converter/page-break.js +92 -0
- package/apps/runtime/dist/engenty/doc-converter/providers/local/index.js +72 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/classifier.js +30 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/index.js +4 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/invoice.js +73 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/receipt.js +63 -0
- package/apps/runtime/dist/engenty/document-scanner/schemas/shared.js +40 -0
- package/apps/runtime/dist/engenty/search-index/chunks.js +164 -0
- package/apps/runtime/dist/engenty/search-index/contracts.js +9 -0
- package/apps/runtime/dist/engenty/shims/connections-sdk.js +39 -0
- package/apps/runtime/dist/engenty/shims/plugin-sdk.js +1 -0
- package/apps/runtime/dist/engenty/shims/web-ingest.js +3 -0
- package/apps/runtime/dist/engenty/web-ingest/ssrf.js +277 -0
- package/apps/runtime/dist/engine/asks.js +60 -0
- package/apps/runtime/dist/engine/events.js +46 -0
- package/apps/runtime/dist/engine/input.js +170 -0
- package/apps/runtime/dist/engine/prepare.js +51 -0
- package/apps/runtime/dist/engine/requirements.js +124 -0
- package/apps/runtime/dist/engine/resources.js +89 -0
- package/apps/runtime/dist/engine/runner.js +560 -0
- package/apps/runtime/dist/engine/steps.js +598 -0
- package/apps/runtime/dist/engine/template.js +183 -0
- package/apps/runtime/dist/engine/types.js +3 -0
- package/apps/runtime/dist/env.js +224 -0
- package/apps/runtime/dist/files/blobs.js +22 -0
- package/apps/runtime/dist/files/objects.js +86 -0
- package/apps/runtime/dist/files/storage.js +95 -0
- package/apps/runtime/dist/geocode.js +58 -0
- package/apps/runtime/dist/harness/claude.js +138 -0
- package/apps/runtime/dist/harness/codex.js +228 -0
- package/apps/runtime/dist/harness/cursor.js +93 -0
- package/apps/runtime/dist/harness/env.js +124 -0
- package/apps/runtime/dist/harness/gemini.js +126 -0
- package/apps/runtime/dist/harness/index.js +99 -0
- package/apps/runtime/dist/harness/model.js +267 -0
- package/apps/runtime/dist/harness/prompt.js +121 -0
- package/apps/runtime/dist/harness/terminal.js +193 -0
- package/apps/runtime/dist/harness/types.js +7 -0
- package/apps/runtime/dist/index.js +76 -0
- package/apps/runtime/dist/limits.js +57 -0
- package/apps/runtime/dist/link-preview.js +59 -0
- package/apps/runtime/dist/manage.js +151 -0
- package/apps/runtime/dist/mcp/auth.js +102 -0
- package/apps/runtime/dist/mcp/bridge.js +59 -0
- package/apps/runtime/dist/mcp/handler.js +42 -0
- package/apps/runtime/dist/mcp/scopes.js +5 -0
- package/apps/runtime/dist/mcp/tools.js +401 -0
- package/apps/runtime/dist/media/ffmpeg.js +121 -0
- package/apps/runtime/dist/media/generate.js +120 -0
- package/apps/runtime/dist/media/marking.js +121 -0
- package/apps/runtime/dist/media/transcribe.js +30 -0
- package/apps/runtime/dist/model-errors.js +3 -0
- package/apps/runtime/dist/models.js +489 -0
- package/apps/runtime/dist/plugin.js +61 -0
- package/apps/runtime/dist/render/chromium.js +85 -0
- package/apps/runtime/dist/render/convert.js +161 -0
- package/apps/runtime/dist/render/downloads.js +277 -0
- package/apps/runtime/dist/render/guard.js +25 -0
- package/apps/runtime/dist/routes/api.js +74 -0
- package/apps/runtime/dist/routes/delivery.js +52 -0
- package/apps/runtime/dist/routes/internal.js +49 -0
- package/apps/runtime/dist/routes/marketplace.js +36 -0
- package/apps/runtime/dist/routes/runs.js +805 -0
- package/apps/runtime/dist/routes/studio.js +702 -0
- package/apps/runtime/dist/routes/wizard-stream.js +30 -0
- package/apps/runtime/dist/running.js +39 -0
- package/apps/runtime/dist/sandbox/agentos.js +191 -0
- package/apps/runtime/dist/sandbox/docker.js +77 -0
- package/apps/runtime/dist/sandbox/index.js +30 -0
- package/apps/runtime/dist/sandbox/types.js +1 -0
- package/apps/runtime/dist/secrets/crypto.js +28 -0
- package/apps/runtime/dist/secrets/signing.js +34 -0
- package/apps/runtime/dist/secrets/vault.js +59 -0
- package/apps/runtime/dist/services/architect.js +92 -0
- package/apps/runtime/dist/services/brand.js +20 -0
- package/apps/runtime/dist/services/cloud.js +52 -0
- package/apps/runtime/dist/services/draft-events.js +27 -0
- package/apps/runtime/dist/services/errors.js +24 -0
- package/apps/runtime/dist/services/files.js +129 -0
- package/apps/runtime/dist/services/links.js +16 -0
- package/apps/runtime/dist/services/marketplace.js +292 -0
- package/apps/runtime/dist/services/package.js +202 -0
- package/apps/runtime/dist/services/profile.js +23 -0
- package/apps/runtime/dist/services/project-files.js +266 -0
- package/apps/runtime/dist/services/project-index.js +109 -0
- package/apps/runtime/dist/services/projects.js +181 -0
- package/apps/runtime/dist/services/runs.js +224 -0
- package/apps/runtime/dist/services/shares.js +129 -0
- package/apps/runtime/dist/services/widgets.js +34 -0
- package/apps/runtime/dist/services/wizards.js +342 -0
- package/apps/runtime/dist/settings.js +16 -0
- package/apps/runtime/dist/store/index.js +279 -0
- package/apps/runtime/dist/tenants/control.js +106 -0
- package/apps/runtime/dist/tenants/tenant.js +26 -0
- package/apps/runtime/dist/tools/browser.js +380 -0
- package/apps/runtime/dist/tools/connector.js +204 -0
- package/apps/runtime/dist/tools/index.js +234 -0
- package/apps/runtime/dist/tools/mail.js +104 -0
- package/apps/runtime/dist/tools/net-guard.js +59 -0
- package/apps/runtime/dist/tools/project.js +53 -0
- package/apps/runtime/dist/tools/shared.js +42 -0
- package/apps/runtime/dist/tools/store.js +238 -0
- package/apps/runtime/dist/update.js +118 -0
- package/apps/runtime/dist/widgets/bundle.js +99 -0
- package/apps/runtime/dist/widgets/render.js +312 -0
- package/apps/runtime/dist/widgets/runtime.js +84 -0
- package/apps/runtime/dist/widgets/script.js +30 -0
- package/apps/runtime/dist/widgets/step.js +192 -0
- package/apps/web/dist/ai-labels/ai-black.svg +20 -0
- package/apps/web/dist/ai-labels/ai-generated-black.svg +31 -0
- package/apps/web/dist/ai-labels/ai-generated-white.svg +32 -0
- package/apps/web/dist/ai-labels/ai-modified-black.svg +30 -0
- package/apps/web/dist/ai-labels/ai-modified-white.svg +31 -0
- package/apps/web/dist/ai-labels/ai-white.svg +21 -0
- package/apps/web/dist/assets/ChangelogDialog-O00vEnbm.js +1 -0
- package/apps/web/dist/assets/ChatPanel-CH9ry0Hp.js +1 -0
- package/apps/web/dist/assets/CreditsDialog-DRzecwUw.js +1 -0
- package/apps/web/dist/assets/EditorPage-BRnBf02Q.js +2 -0
- package/apps/web/dist/assets/FlowPreview-DDOj8IAH.js +1 -0
- package/apps/web/dist/assets/HomePage-CAGTTG3N.js +1 -0
- package/apps/web/dist/assets/ImportWizard-0RRrAnX3.js +1 -0
- package/apps/web/dist/assets/LocalRuntime-BrP-ENHg.css +1 -0
- package/apps/web/dist/assets/LocalRuntime-C-TBYrzs.js +16 -0
- package/apps/web/dist/assets/NewWizardPage-kI90h3_9.js +2 -0
- package/apps/web/dist/assets/PublicRunner-CKRDeIVd.js +1 -0
- package/apps/web/dist/assets/RunnerView-Rc4H8wYH.js +2 -0
- package/apps/web/dist/assets/SettingsPage-CESMuZxq.js +2 -0
- package/apps/web/dist/assets/SetupPage-D1rrYU14.js +1 -0
- package/apps/web/dist/assets/SharePage-COTAmOgh.js +1 -0
- package/apps/web/dist/assets/api-BY28cjGY.js +10 -0
- package/apps/web/dist/assets/arrow-left-UpjOBfQp.js +1 -0
- package/apps/web/dist/assets/arrow-right-CucQycEy.js +1 -0
- package/apps/web/dist/assets/changelog-p3wsaDDS.js +1 -0
- package/apps/web/dist/assets/check-Bjx41xbB.js +1 -0
- package/apps/web/dist/assets/createLucideIcon-DBu2Ovxj.js +1 -0
- package/apps/web/dist/assets/definition-cy7uvY5L.js +55 -0
- package/apps/web/dist/assets/engenty-CWIFpgKq.js +1 -0
- package/apps/web/dist/assets/esm-D1OEhjTK.js +7 -0
- package/apps/web/dist/assets/external-link-CRg6DVdO.js +1 -0
- package/apps/web/dist/assets/fluffy-engenty-CJ3wCQgT.js +456 -0
- package/apps/web/dist/assets/index-CQ_q9iMO.css +1 -0
- package/apps/web/dist/assets/index-D0gxh90t.js +10 -0
- package/apps/web/dist/assets/jsx-runtime-Dk72oS4N.js +1 -0
- package/apps/web/dist/assets/meta-BbSdtTUa.js +1 -0
- package/apps/web/dist/assets/mic-CeqQ89Dj.js +1 -0
- package/apps/web/dist/assets/oss-credits-cPuZUFHD.js +1 -0
- package/apps/web/dist/assets/outputs-D6pxH6ve.js +69 -0
- package/apps/web/dist/assets/plus-HA210wXA.js +1 -0
- package/apps/web/dist/assets/preload-helper-CNEI7N0N.js +1 -0
- package/apps/web/dist/assets/qr-scanner-worker.min-CJaXo3yu.js +98 -0
- package/apps/web/dist/assets/qr-scanner.min-C_UI0gH-.js +1 -0
- package/apps/web/dist/assets/type-BzYgVWKL.js +1 -0
- package/apps/web/dist/assets/ui-Ddwl35A6.js +1 -0
- package/apps/web/dist/assets/upload-DsgLfQ3U.js +1 -0
- package/apps/web/dist/assets/video-Dk1fuTDl.js +1 -0
- package/apps/web/dist/assets/wand-sparkles-Bf97fs8T.js +1 -0
- package/apps/web/dist/embed.js +102 -0
- package/apps/web/dist/favicon.svg +1 -0
- package/apps/web/dist/icons/apple-touch-icon.png +0 -0
- package/apps/web/dist/icons/icon-192.png +0 -0
- package/apps/web/dist/icons/icon-512.png +0 -0
- package/apps/web/dist/icons/icon-maskable-512.png +0 -0
- package/apps/web/dist/index.html +46 -0
- package/apps/web/dist/install.sh +532 -0
- package/apps/web/dist/manifest.webmanifest +20 -0
- package/apps/web/dist/sw.js +15 -0
- package/bin/wizards.mjs +13 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.d.ts +77 -0
- package/node_modules/@engenty-wizards/shared/dist/connectors.js +2 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +1415 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.js +762 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.d.ts +5 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-format.js +132 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.d.ts +11 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns-value.js +209 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +532 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.js +216 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.d.ts +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/index.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.d.ts +14 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-lexical.js +166 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.d.ts +33 -0
- package/node_modules/@engenty-wizards/shared/dist/engenty/search-index/catalog-rank.js +99 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.d.ts +35 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +86 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.d.ts +20 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace-search.js +133 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +298 -0
- package/node_modules/@engenty-wizards/shared/dist/marketplace.js +211 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.d.ts +7 -0
- package/node_modules/@engenty-wizards/shared/dist/merge.js +40 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.d.ts +74 -0
- package/node_modules/@engenty-wizards/shared/dist/projects.js +96 -0
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +219 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +1 -0
- package/node_modules/@engenty-wizards/shared/dist/store.d.ts +100 -0
- package/node_modules/@engenty-wizards/shared/dist/store.js +44 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.d.ts +16 -0
- package/node_modules/@engenty-wizards/shared/dist/workspace.js +51 -0
- package/node_modules/@engenty-wizards/shared/package.json +10 -0
- package/package.json +62 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/wizard.md +8 -0
- package/plugin/skills/build-wizard/SKILL.md +27 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Runs a command; what it prints goes line by line to `onLine` and into the log file.
|
|
6
|
+
* Never rejects for a command that fails: the caller reads `code`.
|
|
7
|
+
*/
|
|
8
|
+
export function runLogged(command, args, options = {}) {
|
|
9
|
+
const { logFile, onLine } = options;
|
|
10
|
+
if (logFile) {
|
|
11
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
12
|
+
appendFileSync(logFile, `\n$ ${command} ${args.join(" ")}\n`);
|
|
13
|
+
}
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
let tail = "";
|
|
16
|
+
let pending = "";
|
|
17
|
+
const take = (chunk) => {
|
|
18
|
+
const text = chunk.toString("utf8");
|
|
19
|
+
tail = (tail + text).slice(-4000);
|
|
20
|
+
if (logFile) {
|
|
21
|
+
appendFileSync(logFile, text);
|
|
22
|
+
}
|
|
23
|
+
pending += text;
|
|
24
|
+
const lines = pending.split(/\r?\n|\r/);
|
|
25
|
+
pending = lines.pop() ?? "";
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
if (line.trim()) {
|
|
28
|
+
onLine?.(line);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const child = spawn(command, args, {
|
|
33
|
+
env: options.env,
|
|
34
|
+
cwd: options.cwd,
|
|
35
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36
|
+
});
|
|
37
|
+
child.stdout.on("data", take);
|
|
38
|
+
child.stderr.on("data", take);
|
|
39
|
+
child.on("error", (error) => {
|
|
40
|
+
tail = `${tail}\n${error.message}`.trim();
|
|
41
|
+
resolve({ code: 127, tail });
|
|
42
|
+
});
|
|
43
|
+
child.on("close", (code) => resolve({ code: code ?? 1, tail: tail.trim() }));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/**
|
|
6
|
+
* Where a local install lives: `~/.engenty/wizards`, inside the folder the engenty framework
|
|
7
|
+
* keeps its own install in. `ENGENTY_HOME` moves both.
|
|
8
|
+
*/
|
|
9
|
+
export function wizardsHome(env = process.env) {
|
|
10
|
+
const base = env.ENGENTY_HOME?.trim() || join(homedir(), ".engenty");
|
|
11
|
+
return join(resolve(base), "wizards");
|
|
12
|
+
}
|
|
13
|
+
export function layout(home = wizardsHome()) {
|
|
14
|
+
return {
|
|
15
|
+
home,
|
|
16
|
+
data: join(home, "data"),
|
|
17
|
+
logs: join(home, "logs"),
|
|
18
|
+
envFile: join(home, ".env"),
|
|
19
|
+
installFile: join(home, "install.json"),
|
|
20
|
+
runtime: join(home, "runtime"),
|
|
21
|
+
tools: join(home, "tools"),
|
|
22
|
+
clients: join(home, "clients"),
|
|
23
|
+
bin: join(home, "bin"),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The folder this code was installed to: it holds `apps/`, `plugin/` and package.json. */
|
|
27
|
+
export const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
28
|
+
export function packageVersion() {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")).version ?? "0.0.0";
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return "0.0.0";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** How this copy got here: by install.sh into the home folder, or by npm/npx or a checkout. */
|
|
37
|
+
export function installedByScript(paths = layout()) {
|
|
38
|
+
try {
|
|
39
|
+
// Node names this file by its real path; the home folder may be reached through a link.
|
|
40
|
+
return packageRoot.startsWith(realpathSync(paths.runtime) + sep);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** This copy is a checkout of the repository, run with `pnpm wizards`. */
|
|
47
|
+
export function isCheckout() {
|
|
48
|
+
return existsSync(join(packageRoot, "pnpm-workspace.yaml"));
|
|
49
|
+
}
|
|
50
|
+
export function readInstallNote(paths = layout()) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(readFileSync(paths.installFile, "utf8"));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function writeInstallNote(note, paths = layout()) {
|
|
59
|
+
writeFileSync(paths.installFile, `${JSON.stringify(note, null, 2)}\n`);
|
|
60
|
+
}
|
|
61
|
+
/** `KEY=value` lines of the install's `.env`, the way the runtime reads them. */
|
|
62
|
+
export function readEnvFile(file) {
|
|
63
|
+
const out = {};
|
|
64
|
+
if (!existsSync(file)) {
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
68
|
+
const match = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
|
|
69
|
+
if (match) {
|
|
70
|
+
out[match[1]] = match[2].replace(/^(['"])(.*)\1$/, "$2");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { accessSync, constants, existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { runtimePath } from "./environment.js";
|
|
7
|
+
const run = promisify(execFile);
|
|
8
|
+
/** The AI clients the runtime can think with, in the order its own setup offers them. */
|
|
9
|
+
export const CLIENTS = [
|
|
10
|
+
{
|
|
11
|
+
id: "codex",
|
|
12
|
+
name: "Codex",
|
|
13
|
+
bin: "codex",
|
|
14
|
+
account: "ChatGPT",
|
|
15
|
+
install: { kind: "npm", pkg: "@openai/codex" },
|
|
16
|
+
app: "ChatGPT",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "claude",
|
|
20
|
+
name: "Claude Code",
|
|
21
|
+
bin: "claude",
|
|
22
|
+
account: "Claude",
|
|
23
|
+
install: { kind: "script", url: "https://claude.ai/install.sh" },
|
|
24
|
+
app: "Claude",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "gemini",
|
|
28
|
+
name: "Gemini CLI",
|
|
29
|
+
bin: "gemini",
|
|
30
|
+
account: "Google",
|
|
31
|
+
install: { kind: "npm", pkg: "@google/gemini-cli" },
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "cursor",
|
|
35
|
+
name: "Cursor Agent",
|
|
36
|
+
bin: "cursor-agent",
|
|
37
|
+
account: "Cursor",
|
|
38
|
+
install: { kind: "script", url: "https://cursor.com/install" },
|
|
39
|
+
app: "Cursor",
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
/** The client's desktop app, when it is on this Mac: its name, e.g. "Claude". */
|
|
43
|
+
export function vendorApp(client, roots = ["/Applications", join(homedir(), "Applications")]) {
|
|
44
|
+
if (process.platform !== "darwin" || !client.app) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const app = client.app;
|
|
48
|
+
return roots.some((root) => existsSync(join(root, `${app}.app`))) ? app : null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The client the setup suggests among those not installed: the one whose app is on this Mac —
|
|
52
|
+
* its subscription is most likely already paid for — else the first.
|
|
53
|
+
*/
|
|
54
|
+
export function suggestedClient(missing) {
|
|
55
|
+
return missing.find((state) => vendorApp(state.client)) ?? missing[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
/** The first file of that name on a PATH that may be run. */
|
|
58
|
+
export function findOnPath(name, path) {
|
|
59
|
+
for (const dir of path.split(delimiter)) {
|
|
60
|
+
if (!dir) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const file = join(dir, name);
|
|
64
|
+
try {
|
|
65
|
+
accessSync(file, constants.X_OK);
|
|
66
|
+
return file;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// not here
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/** What a command prints for `--version`: its last word that looks like one. */
|
|
75
|
+
async function versionOf(file, path) {
|
|
76
|
+
try {
|
|
77
|
+
const { stdout } = await run(file, ["--version"], {
|
|
78
|
+
timeout: 10_000,
|
|
79
|
+
env: { ...process.env, PATH: path, TERM: "dumb" },
|
|
80
|
+
});
|
|
81
|
+
const line = stdout.trim().split("\n").pop()?.trim() ?? "";
|
|
82
|
+
return line.match(/\d+\.\d+[\w.+-]*/)?.[0] ?? (line || null);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function detectClients(paths) {
|
|
89
|
+
const path = runtimePath(paths, process.env);
|
|
90
|
+
return Promise.all(CLIENTS.map(async (client) => {
|
|
91
|
+
const file = findOnPath(client.bin, path);
|
|
92
|
+
// A file that does not answer `--version` is not a client we can run.
|
|
93
|
+
const version = file ? await versionOf(file, path) : null;
|
|
94
|
+
return { client, path: version ? file : null, version };
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
/** The browser the runtime renders PDF and PNG with. */
|
|
98
|
+
export function findChrome(env = process.env) {
|
|
99
|
+
if (env.CHROME_PATH) {
|
|
100
|
+
return existsSync(env.CHROME_PATH) ? env.CHROME_PATH : null;
|
|
101
|
+
}
|
|
102
|
+
if (process.platform === "darwin") {
|
|
103
|
+
const apps = [
|
|
104
|
+
"Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
105
|
+
"Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
|
106
|
+
"Chromium.app/Contents/MacOS/Chromium",
|
|
107
|
+
"Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
108
|
+
];
|
|
109
|
+
const roots = ["/Applications", join(homedir(), "Applications")];
|
|
110
|
+
for (const app of apps) {
|
|
111
|
+
for (const root of roots) {
|
|
112
|
+
if (existsSync(join(root, app))) {
|
|
113
|
+
return join(root, app);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
for (const name of ["google-chrome", "chromium", "chromium-browser", "microsoft-edge"]) {
|
|
120
|
+
const file = findOnPath(name, env.PATH ?? "");
|
|
121
|
+
if (file) {
|
|
122
|
+
return file;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
export async function findFfmpeg(env = process.env) {
|
|
128
|
+
const path = env.PATH ?? "";
|
|
129
|
+
const file = env.FFMPEG_PATH || findOnPath("ffmpeg", path);
|
|
130
|
+
if (!file) {
|
|
131
|
+
return { path: null, version: null };
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await run(file, ["-version"], { timeout: 10_000 });
|
|
135
|
+
return { path: file, version: stdout.match(/ffmpeg version (\S+)/)?.[1] ?? null };
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { path: null, version: null };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Node 24.11 or newer: what the runtime is built for. */
|
|
142
|
+
export function nodeIsCurrent(version = process.versions.node) {
|
|
143
|
+
const [major, minor] = version.split(".").map(Number);
|
|
144
|
+
return major > 24 || (major === 24 && minor >= 11);
|
|
145
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { layout, packageVersion, readInstallNote } from "./home.js";
|
|
2
|
+
import { CLIENTS, nodeIsCurrent } from "./machine.js";
|
|
3
|
+
import { command, setup } from "./setup.js";
|
|
4
|
+
import { open, start } from "./start.js";
|
|
5
|
+
import { autostart, status, stop } from "./status.js";
|
|
6
|
+
import { update } from "./update.js";
|
|
7
|
+
const HELP = `engenty wizards ${packageVersion()}
|
|
8
|
+
|
|
9
|
+
${command()} [start] start it and open the studio; the first time, the setup runs first
|
|
10
|
+
${command()} setup guided setup: an AI client to think with, ffmpeg, start at login
|
|
11
|
+
${command()} open let this browser into the running studio (--print: only show the link)
|
|
12
|
+
${command()} status what is installed and what runs
|
|
13
|
+
${command()} doctor status, and what is wrong
|
|
14
|
+
${command()} stop stop it; the data is kept
|
|
15
|
+
${command()} update the newest version
|
|
16
|
+
${command()} autostart start it at login: on, off (macOS: a LaunchAgent; Linux: a systemd service)
|
|
17
|
+
|
|
18
|
+
--yes, -y ask nothing and install nothing optional
|
|
19
|
+
--client <name> install this AI client without asking: ${CLIENTS.map((c) => c.id).join(", ")}
|
|
20
|
+
--no-open start without opening the browser
|
|
21
|
+
--version, --help
|
|
22
|
+
|
|
23
|
+
Everything lives in ~/.engenty/wizards (ENGENTY_HOME moves it); settings go into its .env.
|
|
24
|
+
`;
|
|
25
|
+
function parse(argv) {
|
|
26
|
+
const args = {
|
|
27
|
+
command: "",
|
|
28
|
+
value: "",
|
|
29
|
+
yes: false,
|
|
30
|
+
open: true,
|
|
31
|
+
print: false,
|
|
32
|
+
clients: [],
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const arg = argv[i];
|
|
36
|
+
if (arg === "--yes" || arg === "-y") {
|
|
37
|
+
args.yes = true;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--no-open") {
|
|
40
|
+
args.open = false;
|
|
41
|
+
}
|
|
42
|
+
else if (arg === "--print") {
|
|
43
|
+
args.print = true;
|
|
44
|
+
}
|
|
45
|
+
else if (arg === "--client") {
|
|
46
|
+
const id = argv[++i];
|
|
47
|
+
if (!CLIENTS.some((client) => client.id === id)) {
|
|
48
|
+
return `--client takes one of: ${CLIENTS.map((c) => c.id).join(", ")}`;
|
|
49
|
+
}
|
|
50
|
+
args.clients.push(id);
|
|
51
|
+
}
|
|
52
|
+
else if (arg === "--help" || arg === "-h") {
|
|
53
|
+
args.command = "help";
|
|
54
|
+
}
|
|
55
|
+
else if (arg === "--version" || arg === "-v") {
|
|
56
|
+
args.command = "version";
|
|
57
|
+
}
|
|
58
|
+
else if (arg.startsWith("-")) {
|
|
59
|
+
return `Unknown option ${arg}.`;
|
|
60
|
+
}
|
|
61
|
+
else if (args.command === "autostart" && !args.value) {
|
|
62
|
+
args.value = arg;
|
|
63
|
+
}
|
|
64
|
+
else if (args.command) {
|
|
65
|
+
return `Unexpected argument ${arg}.`;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
args.command = arg;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
args.command ||= "start";
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
async function main(argv) {
|
|
75
|
+
const args = parse(argv);
|
|
76
|
+
if (typeof args === "string") {
|
|
77
|
+
console.error(`${args}\n\n${HELP}`);
|
|
78
|
+
return 2;
|
|
79
|
+
}
|
|
80
|
+
const paths = layout();
|
|
81
|
+
const setupOptions = { yes: args.yes, clients: args.clients };
|
|
82
|
+
switch (args.command) {
|
|
83
|
+
case "help":
|
|
84
|
+
console.log(HELP);
|
|
85
|
+
return 0;
|
|
86
|
+
case "version":
|
|
87
|
+
console.log(packageVersion());
|
|
88
|
+
return 0;
|
|
89
|
+
case "start": {
|
|
90
|
+
// The first start on a terminal begins with the setup.
|
|
91
|
+
const first = !readInstallNote(paths).setupAt;
|
|
92
|
+
if (first && !args.yes && process.stdin.isTTY && process.stdout.isTTY) {
|
|
93
|
+
const next = await setup(paths, setupOptions);
|
|
94
|
+
if (next !== "start") {
|
|
95
|
+
return next === null ? 130 : 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return start(paths, { open: args.open });
|
|
99
|
+
}
|
|
100
|
+
case "setup": {
|
|
101
|
+
const next = await setup(paths, setupOptions);
|
|
102
|
+
return next === "start" ? start(paths, { open: args.open }) : next === null ? 130 : 0;
|
|
103
|
+
}
|
|
104
|
+
case "open":
|
|
105
|
+
return open(paths, args.print);
|
|
106
|
+
case "status":
|
|
107
|
+
return status(paths, false);
|
|
108
|
+
case "doctor":
|
|
109
|
+
return status(paths, true);
|
|
110
|
+
case "stop":
|
|
111
|
+
return stop(paths);
|
|
112
|
+
case "update":
|
|
113
|
+
return update(paths);
|
|
114
|
+
case "autostart":
|
|
115
|
+
return autostart(paths, args.value);
|
|
116
|
+
default:
|
|
117
|
+
console.error(`Unknown command ${args.command}.\n\n${HELP}`);
|
|
118
|
+
return 2;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!nodeIsCurrent()) {
|
|
122
|
+
console.error(`engenty wizards needs Node.js 24.11 or newer; this is ${process.version}.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
process.exitCode = await main(process.argv.slice(2));
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { delimiter, join } from "node:path";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
5
|
+
import { autostartState, setAutostart } from "./autostart.js";
|
|
6
|
+
import { installClient, installCommand } from "./clients.js";
|
|
7
|
+
import { runLogged } from "./exec.js";
|
|
8
|
+
import { installedByScript, isCheckout, packageVersion, readInstallNote, writeInstallNote, } from "./home.js";
|
|
9
|
+
import { CLIENTS, detectClients, findChrome, findFfmpeg, findOnPath, suggestedClient, vendorApp, } from "./machine.js";
|
|
10
|
+
import { badge, cyan, dim, tilde } from "./ui.js";
|
|
11
|
+
/** The studio as an app of its own: Chrome (or Edge) installs it from the page. */
|
|
12
|
+
const CHROME_APP = "Tip: in Chrome or Edge the studio installs as an app, in a window of its own with its own icon. The studio offers it at the top; or use the install icon in the address bar.";
|
|
13
|
+
/** How this install is started, as the person types it. */
|
|
14
|
+
export const command = () => installedByScript() ? "wizards" : isCheckout() ? "pnpm wizards" : "npx wizards";
|
|
15
|
+
/** A spinner on a terminal; where nobody watches, only what came of it. */
|
|
16
|
+
function busy(message) {
|
|
17
|
+
if (!process.stdout.isTTY) {
|
|
18
|
+
return { stop: (text) => p.log.step(text), fail: (text) => p.log.warn(text) };
|
|
19
|
+
}
|
|
20
|
+
const spinner = p.spinner();
|
|
21
|
+
spinner.start(message);
|
|
22
|
+
return {
|
|
23
|
+
stop: (text) => spinner.stop(text),
|
|
24
|
+
fail: (text) => spinner.error(text),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function cancelled(value) {
|
|
28
|
+
if (p.isCancel(value)) {
|
|
29
|
+
p.cancel(`Setup stopped. Run it again with \`${command()} setup\`.`);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
async function install(client, paths) {
|
|
35
|
+
const task = p.taskLog({ title: `Installing ${client.name}`, limit: 6 });
|
|
36
|
+
task.message(dim(installCommand(client, paths)));
|
|
37
|
+
const { code, tail } = await installClient(client, paths, (line) => task.message(line));
|
|
38
|
+
const installed = (await detectClients(paths)).find((state) => state.client.id === client.id);
|
|
39
|
+
if (code === 0 && installed?.path) {
|
|
40
|
+
task.success(`${client.name} ${installed.version ?? ""} installed`.trim());
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
task.error(`${client.name} was not installed${code === 0 ? ": its command is not on the PATH" : ""}. ${tail.split("\n").pop() ?? ""}`);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
async function clientsStep(paths, options, interactive) {
|
|
47
|
+
const spinner = busy("Looking for AI clients on this machine");
|
|
48
|
+
const found = await detectClients(paths);
|
|
49
|
+
const installed = found.filter((state) => state.path);
|
|
50
|
+
const missing = found.filter((state) => !state.path);
|
|
51
|
+
spinner.stop(installed.length
|
|
52
|
+
? `Thinks with: ${installed.map((s) => `${s.client.name} ${s.version}`).join(", ")}`
|
|
53
|
+
: "No AI client on this machine yet");
|
|
54
|
+
if (!installed.length) {
|
|
55
|
+
p.log.message(dim("engenty wizards thinks with one, on the subscription you already have."));
|
|
56
|
+
}
|
|
57
|
+
let wanted = missing.filter((s) => options.clients.includes(s.client.id));
|
|
58
|
+
if (interactive && missing.length && !wanted.length) {
|
|
59
|
+
const suggested = suggestedClient(missing);
|
|
60
|
+
// A client whose app is on this Mac comes first: its subscription is most likely paid for.
|
|
61
|
+
const offered = [...missing].sort((a, b) => Number(!vendorApp(a.client)) - Number(!vendorApp(b.client)));
|
|
62
|
+
const answer = await p.multiselect({
|
|
63
|
+
message: installed.length
|
|
64
|
+
? "Install another AI client? (space to pick, enter to go on)"
|
|
65
|
+
: "Which one shall I install?",
|
|
66
|
+
options: offered.map(({ client }) => {
|
|
67
|
+
const app = vendorApp(client);
|
|
68
|
+
return {
|
|
69
|
+
value: client.id,
|
|
70
|
+
label: client.name,
|
|
71
|
+
hint: [
|
|
72
|
+
`${client.account} subscription`,
|
|
73
|
+
app ? `the ${app} app is on this Mac` : "",
|
|
74
|
+
!installed.length && client.id === suggested?.client.id ? "recommended" : "",
|
|
75
|
+
]
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.join(", "),
|
|
78
|
+
};
|
|
79
|
+
}),
|
|
80
|
+
initialValues: installed.length || !suggested ? [] : [suggested.client.id],
|
|
81
|
+
required: false,
|
|
82
|
+
});
|
|
83
|
+
if (cancelled(answer)) {
|
|
84
|
+
return "cancelled";
|
|
85
|
+
}
|
|
86
|
+
wanted = missing.filter((s) => answer.includes(s.client.id));
|
|
87
|
+
}
|
|
88
|
+
let any = installed.length > 0;
|
|
89
|
+
for (const { client } of wanted) {
|
|
90
|
+
any = (await install(client, paths)) || any;
|
|
91
|
+
}
|
|
92
|
+
if (any) {
|
|
93
|
+
p.log.info("You sign the client in on the first page of the studio.");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
p.log.warn(`No AI client yet: the first page of the studio installs one, or takes an API key (Vercel AI Gateway, OpenAI, Anthropic) or a local model (Ollama).\n${dim(`Or here, later: ${CLIENTS.map((c) => c.name).join(", ")} with \`${command()} setup\`.`)}`);
|
|
97
|
+
}
|
|
98
|
+
return any;
|
|
99
|
+
}
|
|
100
|
+
async function toolsStep(interactive) {
|
|
101
|
+
const chrome = findChrome();
|
|
102
|
+
if (chrome) {
|
|
103
|
+
p.log.success(`Browser for PDF and PNG: ${dim(tilde(chrome))}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
p.log.warn("No Chrome, Edge or Chromium: PDF and PNG exports and browser steps need one.\nhttps://www.google.com/chrome");
|
|
107
|
+
}
|
|
108
|
+
const ffmpeg = await findFfmpeg();
|
|
109
|
+
if (ffmpeg.path) {
|
|
110
|
+
p.log.success(`ffmpeg ${ffmpeg.version ?? ""} for videos`.replace(" ", " "));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const brew = findOnPath("brew", process.env.PATH ?? "");
|
|
114
|
+
if (!interactive || !brew) {
|
|
115
|
+
p.log.warn(`No ffmpeg: MP4 videos of animated widgets need it.${brew ? `\n${dim("brew install ffmpeg")}` : ""}`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const answer = await p.confirm({
|
|
119
|
+
message: "No ffmpeg (for MP4 videos of animated widgets). Install it with Homebrew?",
|
|
120
|
+
initialValue: false,
|
|
121
|
+
});
|
|
122
|
+
if (cancelled(answer)) {
|
|
123
|
+
return "cancelled";
|
|
124
|
+
}
|
|
125
|
+
if (answer) {
|
|
126
|
+
const task = p.taskLog({ title: "Installing ffmpeg", limit: 6 });
|
|
127
|
+
const { code, tail } = await runLogged(brew, ["install", "ffmpeg"], {
|
|
128
|
+
onLine: (line) => task.message(line),
|
|
129
|
+
});
|
|
130
|
+
if (code === 0) {
|
|
131
|
+
task.success("ffmpeg installed");
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
task.error(`ffmpeg was not installed. ${tail.split("\n").pop() ?? ""}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Starting at login: on a Mac a LaunchAgent starts the runtime, on Linux a systemd user
|
|
140
|
+
* service does. Asked, never assumed; `autostart off` undoes it.
|
|
141
|
+
*/
|
|
142
|
+
async function autostartStep(paths, interactive) {
|
|
143
|
+
const state = await autostartState(paths);
|
|
144
|
+
if ("unavailable" in state) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (state.on) {
|
|
148
|
+
p.log.success(`Starts at login: ${dim(state.how)}`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (!interactive) {
|
|
152
|
+
p.log.info(dim(`To start it at login: \`${command()} autostart on\``));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const answer = await p.confirm({
|
|
156
|
+
message: "Start engenty wizards when you log in? It keeps running in the background, so links and AI clients always reach it.",
|
|
157
|
+
initialValue: true,
|
|
158
|
+
});
|
|
159
|
+
if (cancelled(answer)) {
|
|
160
|
+
return "cancelled";
|
|
161
|
+
}
|
|
162
|
+
if (!answer) {
|
|
163
|
+
p.log.info(dim(`Later: \`${command()} autostart on\``));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const after = await setAutostart(paths, true);
|
|
168
|
+
if ("on" in after && after.on) {
|
|
169
|
+
p.log.success(`Starts at login: ${dim(after.how)}`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
p.log.warn("Starting at login could not be switched on.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
p.log.warn(`Starting at login could not be switched on. ${error.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** The line that puts `~/.local/bin` on the PATH, and the shell file it belongs in. */
|
|
180
|
+
function pathLine() {
|
|
181
|
+
const shell = process.env.SHELL ?? "";
|
|
182
|
+
if (shell.endsWith("fish")) {
|
|
183
|
+
return {
|
|
184
|
+
file: join(homedir(), ".config/fish/config.fish"),
|
|
185
|
+
line: "fish_add_path ~/.local/bin",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const file = shell.endsWith("zsh")
|
|
189
|
+
? ".zshrc"
|
|
190
|
+
: process.platform === "darwin"
|
|
191
|
+
? ".bash_profile"
|
|
192
|
+
: ".bashrc";
|
|
193
|
+
return { file: join(homedir(), file), line: 'export PATH="$HOME/.local/bin:$PATH"' };
|
|
194
|
+
}
|
|
195
|
+
/** An install made by install.sh has its command in `~/.local/bin`: is that on the PATH? */
|
|
196
|
+
async function pathStep(interactive) {
|
|
197
|
+
const local = join(homedir(), ".local", "bin");
|
|
198
|
+
if (!installedByScript() || (process.env.PATH ?? "").split(delimiter).includes(local)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const { file, line } = pathLine();
|
|
202
|
+
if (existsSync(file) && readFileSync(file, "utf8").includes(".local/bin")) {
|
|
203
|
+
p.log.info(`Open a new terminal to use the \`wizards\` command.`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const hint = `Until then: ${tilde(join(local, "wizards"))}`;
|
|
207
|
+
if (!interactive) {
|
|
208
|
+
p.log.warn(`~/.local/bin is not on your PATH. Add to ${tilde(file)}:\n${line}\n${dim(hint)}`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const answer = await p.confirm({
|
|
212
|
+
message: `The \`wizards\` command is in ~/.local/bin, which is not on your PATH. Add it in ${tilde(file)}?`,
|
|
213
|
+
initialValue: true,
|
|
214
|
+
});
|
|
215
|
+
if (cancelled(answer)) {
|
|
216
|
+
return "cancelled";
|
|
217
|
+
}
|
|
218
|
+
if (answer) {
|
|
219
|
+
appendFileSync(file, `\n# engenty wizards\n${line}\n`);
|
|
220
|
+
p.log.success(`Added to ${tilde(file)}. It works in a new terminal.\n${dim(hint)}`);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
p.log.info(hint);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The guided setup: what this machine has, what is missing, and what to install — an AI client
|
|
228
|
+
* to think with, ffmpeg, starting at login. Returns what to do next, or null when it was stopped.
|
|
229
|
+
*/
|
|
230
|
+
export async function setup(paths, options) {
|
|
231
|
+
const interactive = !options.yes && Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
232
|
+
const version = packageVersion();
|
|
233
|
+
p.intro(`${badge("engenty wizards")} ${dim(version)}`);
|
|
234
|
+
p.log.message([
|
|
235
|
+
`Install ${tilde(paths.home)}${installedByScript() ? "" : dim(` (data; the code runs from ${isCheckout() ? "this checkout" : "npm"})`)}`,
|
|
236
|
+
`Node ${process.version} ${dim(tilde(process.execPath))}`,
|
|
237
|
+
].join("\n"));
|
|
238
|
+
if ((await clientsStep(paths, options, interactive)) === "cancelled") {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
if ((await toolsStep(interactive)) === "cancelled") {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
if ((await autostartStep(paths, interactive)) === "cancelled") {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
if ((await pathStep(interactive)) === "cancelled") {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
writeInstallNote({ ...readInstallNote(paths), setupAt: new Date().toISOString(), version }, paths);
|
|
251
|
+
p.log.info(CHROME_APP);
|
|
252
|
+
if (!interactive) {
|
|
253
|
+
p.outro(`Start it with ${cyan(command())}`);
|
|
254
|
+
return "later";
|
|
255
|
+
}
|
|
256
|
+
const next = await p.select({
|
|
257
|
+
message: "All set. What next?",
|
|
258
|
+
options: [
|
|
259
|
+
{ value: "start", label: "Start it here", hint: "the studio opens in your browser" },
|
|
260
|
+
{ value: "later", label: "Not now" },
|
|
261
|
+
],
|
|
262
|
+
});
|
|
263
|
+
if (cancelled(next)) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
if (next === "later") {
|
|
267
|
+
p.outro(`Start it with ${cyan(command())}`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
p.outro("Starting engenty wizards");
|
|
271
|
+
}
|
|
272
|
+
return next;
|
|
273
|
+
}
|