@tarout/cli 0.7.2 → 0.9.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/{api-QVUO7EWV.js → api-YFXACHOB.js} +2 -2
- package/dist/{chunk-Y6TWR3XZ.js → chunk-CZPJYUNC.js} +1 -1
- package/dist/{chunk-DI66W4S5.js → chunk-OPPZGNJX.js} +1 -1
- package/dist/{chunk-K7JK5HIL.js → chunk-WKITMZ4U.js} +6 -0
- package/dist/index.js +359 -135
- package/dist/{prompts-JH6YBHHV.js → prompts-D72VJYWE.js} +2 -2
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
createApiClient,
|
|
3
3
|
getApiClient,
|
|
4
4
|
resetApiClient
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CZPJYUNC.js";
|
|
6
6
|
import "./chunk-5DAFGMBH.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-WKITMZ4U.js";
|
|
8
8
|
export {
|
|
9
9
|
createApiClient,
|
|
10
10
|
getApiClient,
|
|
@@ -106,6 +106,11 @@ function success(message) {
|
|
|
106
106
|
console.log(colors.success(`\u2713 ${message}`));
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
function warn(message) {
|
|
110
|
+
if (!globalOptions.json) {
|
|
111
|
+
console.log(colors.warn(`\u26A0 ${message}`));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
109
114
|
function error(message, suggestions) {
|
|
110
115
|
if (globalOptions.json) {
|
|
111
116
|
outputJson(jsonError("ERROR", message, suggestions));
|
|
@@ -197,6 +202,7 @@ export {
|
|
|
197
202
|
getStatusBadge,
|
|
198
203
|
log,
|
|
199
204
|
success,
|
|
205
|
+
warn,
|
|
200
206
|
error,
|
|
201
207
|
table,
|
|
202
208
|
outputData,
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
handleError,
|
|
14
14
|
platformFetch,
|
|
15
15
|
resetApiClient
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-CZPJYUNC.js";
|
|
17
17
|
import {
|
|
18
18
|
clearConfig,
|
|
19
19
|
getApiUrl,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
password,
|
|
35
35
|
promptOrEmit,
|
|
36
36
|
select
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-OPPZGNJX.js";
|
|
38
38
|
import {
|
|
39
39
|
ExitCode,
|
|
40
40
|
box,
|
|
@@ -52,8 +52,9 @@ import {
|
|
|
52
52
|
setGlobalOptions,
|
|
53
53
|
shouldSkipConfirmation,
|
|
54
54
|
success,
|
|
55
|
-
table
|
|
56
|
-
|
|
55
|
+
table,
|
|
56
|
+
warn
|
|
57
|
+
} from "./chunk-WKITMZ4U.js";
|
|
57
58
|
|
|
58
59
|
// src/index.ts
|
|
59
60
|
import { Command } from "commander";
|
|
@@ -61,7 +62,7 @@ import { Command } from "commander";
|
|
|
61
62
|
// package.json
|
|
62
63
|
var package_default = {
|
|
63
64
|
name: "@tarout/cli",
|
|
64
|
-
version: "0.
|
|
65
|
+
version: "0.9.0",
|
|
65
66
|
description: "Tarout CLI \u2014 the Saudi cloud platform for coding agents",
|
|
66
67
|
type: "module",
|
|
67
68
|
bin: {
|
|
@@ -632,7 +633,7 @@ Root access: ${hasAccess ? colors.success("yes") : colors.error("no")}
|
|
|
632
633
|
try {
|
|
633
634
|
if (!isLoggedIn()) throw new AuthError();
|
|
634
635
|
if (!shouldSkipConfirmation()) {
|
|
635
|
-
const { confirm: confirmFn } = await import("./prompts-
|
|
636
|
+
const { confirm: confirmFn } = await import("./prompts-D72VJYWE.js");
|
|
636
637
|
const ok = await confirmFn(
|
|
637
638
|
`Remove user "${userId}" from the organization?`,
|
|
638
639
|
false,
|
|
@@ -659,7 +660,7 @@ Root access: ${hasAccess ? colors.success("yes") : colors.error("no")}
|
|
|
659
660
|
account.command("assign-permissions").argument("<user-id>", "User ID").option("--role <role>", "Role: admin, member").description("Assign permissions/role to a member (org owner only)").action(async (userId, options) => {
|
|
660
661
|
try {
|
|
661
662
|
if (!isLoggedIn()) throw new AuthError();
|
|
662
|
-
const { select: selectFn } = await import("./prompts-
|
|
663
|
+
const { select: selectFn } = await import("./prompts-D72VJYWE.js");
|
|
663
664
|
const role = options.role || await selectFn(
|
|
664
665
|
"Role:",
|
|
665
666
|
[
|
|
@@ -724,6 +725,192 @@ Root access: ${hasAccess ? colors.success("yes") : colors.error("no")}
|
|
|
724
725
|
});
|
|
725
726
|
}
|
|
726
727
|
|
|
728
|
+
// src/commands/agent.ts
|
|
729
|
+
import { resolve } from "path";
|
|
730
|
+
|
|
731
|
+
// src/lib/agent-scaffold.ts
|
|
732
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
733
|
+
import { join } from "path";
|
|
734
|
+
var AGENT_TYPES = [
|
|
735
|
+
"claude",
|
|
736
|
+
"codex",
|
|
737
|
+
"cursor",
|
|
738
|
+
"other"
|
|
739
|
+
];
|
|
740
|
+
var TAROUT_ALLOW_ENTRY = "Bash(tarout:*)";
|
|
741
|
+
var BLOCK_BEGIN = "<!-- BEGIN TAROUT -->";
|
|
742
|
+
var BLOCK_END = "<!-- END TAROUT -->";
|
|
743
|
+
var TAROUT_AGENT_BLOCK = `${BLOCK_BEGIN}
|
|
744
|
+
## Tarout
|
|
745
|
+
|
|
746
|
+
This project deploys to [Tarout](https://tarout.sa) (PaaS). Use the **Tarout CLI**
|
|
747
|
+
for all deployment, database, storage, environment-variable, and domain operations \u2014
|
|
748
|
+
don't hand-edit infrastructure.
|
|
749
|
+
|
|
750
|
+
- **Deploy** the current folder: \`tarout up --json --yes\` (inspects \u2192 builds \u2192 deploys;
|
|
751
|
+
read the final JSON envelope's \`success\` and \`data.url\`).
|
|
752
|
+
- **Re-deploy** a linked app: \`tarout deploy --wait\`.
|
|
753
|
+
- **Run locally** with cloud env vars: \`tarout dev\`.
|
|
754
|
+
- **Full agent guide:** https://tarout.sa/docs/for-ai/onboarding
|
|
755
|
+
|
|
756
|
+
Run Tarout commands with \`--json\` for machine-readable output. On a \`NEEDS_UPGRADE\`
|
|
757
|
+
error, surface it to the user and follow the upgrade flow instead of auto-retrying.
|
|
758
|
+
Ask before destructive actions (delete, rollback, revealing secrets).
|
|
759
|
+
${BLOCK_END}`;
|
|
760
|
+
function markdownTargetFor(agent) {
|
|
761
|
+
return agent === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
762
|
+
}
|
|
763
|
+
function hasMarkers(content) {
|
|
764
|
+
return content.includes(BLOCK_BEGIN) && content.includes(BLOCK_END);
|
|
765
|
+
}
|
|
766
|
+
function replaceBlock(content, block) {
|
|
767
|
+
const begin = content.indexOf(BLOCK_BEGIN);
|
|
768
|
+
const end = content.indexOf(BLOCK_END);
|
|
769
|
+
if (begin === -1 || end === -1 || end < begin) return content;
|
|
770
|
+
return `${content.slice(0, begin)}${block}${content.slice(end + BLOCK_END.length)}`;
|
|
771
|
+
}
|
|
772
|
+
function upsertMarkdownBlock(filePath, block) {
|
|
773
|
+
if (!existsSync(filePath)) {
|
|
774
|
+
writeFileSync(filePath, `${block}
|
|
775
|
+
`, "utf-8");
|
|
776
|
+
return "created";
|
|
777
|
+
}
|
|
778
|
+
const existing = readFileSync(filePath, "utf-8");
|
|
779
|
+
if (hasMarkers(existing)) {
|
|
780
|
+
const replaced = replaceBlock(existing, block);
|
|
781
|
+
if (replaced === existing) return "unchanged";
|
|
782
|
+
writeFileSync(filePath, replaced, "utf-8");
|
|
783
|
+
return "updated";
|
|
784
|
+
}
|
|
785
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
786
|
+
writeFileSync(filePath, `${existing}${separator}${block}
|
|
787
|
+
`, "utf-8");
|
|
788
|
+
return "appended";
|
|
789
|
+
}
|
|
790
|
+
function mergeClaudeSettings(claudeDir) {
|
|
791
|
+
const settingsPath = join(claudeDir, "settings.local.json");
|
|
792
|
+
const relPath = join(".claude", "settings.local.json");
|
|
793
|
+
if (!existsSync(settingsPath)) {
|
|
794
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
795
|
+
const settings2 = {
|
|
796
|
+
permissions: { allow: [TAROUT_ALLOW_ENTRY] }
|
|
797
|
+
};
|
|
798
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings2, null, 2)}
|
|
799
|
+
`, "utf-8");
|
|
800
|
+
return { path: relPath, action: "created" };
|
|
801
|
+
}
|
|
802
|
+
let settings;
|
|
803
|
+
try {
|
|
804
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
805
|
+
} catch {
|
|
806
|
+
return {
|
|
807
|
+
path: relPath,
|
|
808
|
+
action: "skipped",
|
|
809
|
+
reason: "existing settings.local.json is not valid JSON; left untouched"
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
if (typeof settings !== "object" || settings === null) {
|
|
813
|
+
return {
|
|
814
|
+
path: relPath,
|
|
815
|
+
action: "skipped",
|
|
816
|
+
reason: "existing settings.local.json is not a JSON object; left untouched"
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
const permissions = settings.permissions ??= {};
|
|
820
|
+
const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
821
|
+
if (allow.includes(TAROUT_ALLOW_ENTRY)) {
|
|
822
|
+
return { path: relPath, action: "unchanged" };
|
|
823
|
+
}
|
|
824
|
+
permissions.allow = [...allow, TAROUT_ALLOW_ENTRY];
|
|
825
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
826
|
+
`, "utf-8");
|
|
827
|
+
return { path: relPath, action: "updated" };
|
|
828
|
+
}
|
|
829
|
+
function scaffoldAgentConfig(options) {
|
|
830
|
+
const { cwd, agent } = options;
|
|
831
|
+
const files = [];
|
|
832
|
+
const mdName = markdownTargetFor(agent);
|
|
833
|
+
files.push({
|
|
834
|
+
path: mdName,
|
|
835
|
+
action: upsertMarkdownBlock(join(cwd, mdName), TAROUT_AGENT_BLOCK)
|
|
836
|
+
});
|
|
837
|
+
if (agent === "claude") {
|
|
838
|
+
files.push(mergeClaudeSettings(join(cwd, ".claude")));
|
|
839
|
+
}
|
|
840
|
+
return {
|
|
841
|
+
agent,
|
|
842
|
+
files,
|
|
843
|
+
nextSteps: ["tarout up --json --yes"]
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/commands/agent.ts
|
|
848
|
+
function parseAgent(value) {
|
|
849
|
+
const agent = (value ?? "claude").toLowerCase();
|
|
850
|
+
if (AGENT_TYPES.includes(agent)) {
|
|
851
|
+
return agent;
|
|
852
|
+
}
|
|
853
|
+
throw new CliError(
|
|
854
|
+
`Invalid agent "${value}". Use one of: ${AGENT_TYPES.join(", ")}.`,
|
|
855
|
+
ExitCode.INVALID_ARGUMENTS
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
function registerAgentCommands(program2) {
|
|
859
|
+
const agent = program2.command("agent").description("Configure coding agents to use the Tarout CLI");
|
|
860
|
+
agent.command("init").argument("[path]", "Project directory (defaults to current)").description(
|
|
861
|
+
"Write agent instructions (CLAUDE.md/AGENTS.md) and a Bash(tarout:*) permission allowlist so a coding agent can drive Tarout"
|
|
862
|
+
).option(
|
|
863
|
+
"--agent <type>",
|
|
864
|
+
`Coding agent to configure: ${AGENT_TYPES.join(", ")}`,
|
|
865
|
+
"claude"
|
|
866
|
+
).action(async (cwdArg, options) => {
|
|
867
|
+
try {
|
|
868
|
+
const cwd = cwdArg ? resolve(cwdArg) : process.cwd();
|
|
869
|
+
const agentType = parseAgent(options.agent);
|
|
870
|
+
const result = scaffoldAgentConfig({ cwd, agent: agentType });
|
|
871
|
+
if (isJsonMode()) {
|
|
872
|
+
for (const file of result.files) {
|
|
873
|
+
outputJsonLine({
|
|
874
|
+
type: "event",
|
|
875
|
+
event: "file_written",
|
|
876
|
+
path: file.path,
|
|
877
|
+
action: file.action,
|
|
878
|
+
...file.reason ? { reason: file.reason } : {}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
outputJsonLine({
|
|
882
|
+
type: "result",
|
|
883
|
+
ok: true,
|
|
884
|
+
agent: result.agent,
|
|
885
|
+
files: result.files,
|
|
886
|
+
nextSteps: result.nextSteps
|
|
887
|
+
});
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
success("Coding agents configured");
|
|
891
|
+
box(
|
|
892
|
+
`Agent: ${colors.cyan(result.agent)}`,
|
|
893
|
+
result.files.map((file) => {
|
|
894
|
+
const label = `${colors.bold(file.action)} ${file.path}`;
|
|
895
|
+
return file.reason ? `${label} \u2014 ${colors.dim(file.reason)}` : label;
|
|
896
|
+
})
|
|
897
|
+
);
|
|
898
|
+
for (const file of result.files) {
|
|
899
|
+
if (file.action === "skipped") {
|
|
900
|
+
warn(`${file.path} was left untouched (${file.reason}).`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
log("Next steps:");
|
|
904
|
+
for (const step of result.nextSteps) {
|
|
905
|
+
log(` ${colors.dim(step)}`);
|
|
906
|
+
}
|
|
907
|
+
log("");
|
|
908
|
+
} catch (err) {
|
|
909
|
+
handleError(err);
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
|
|
727
914
|
// src/commands/ai.ts
|
|
728
915
|
function registerAiCommands(program2) {
|
|
729
916
|
const ai = program2.command("ai").description("Manage AI Gateway models and API keys");
|
|
@@ -1544,7 +1731,7 @@ function registerAppsCommands(program2) {
|
|
|
1544
1731
|
throw new NotFoundError("Application", appIdentifier, suggestions);
|
|
1545
1732
|
}
|
|
1546
1733
|
if (!shouldSkipConfirmation()) {
|
|
1547
|
-
const { confirm: confirm2 } = await import("./prompts-
|
|
1734
|
+
const { confirm: confirm2 } = await import("./prompts-D72VJYWE.js");
|
|
1548
1735
|
const confirmed = await confirm2(
|
|
1549
1736
|
`Stop application "${app.name}"?`,
|
|
1550
1737
|
false,
|
|
@@ -1862,7 +2049,7 @@ function registerAppsCommands(program2) {
|
|
|
1862
2049
|
throw new NotFoundError("Application", appIdentifier);
|
|
1863
2050
|
}
|
|
1864
2051
|
if (!shouldSkipConfirmation()) {
|
|
1865
|
-
const { confirm: confirm2 } = await import("./prompts-
|
|
2052
|
+
const { confirm: confirm2 } = await import("./prompts-D72VJYWE.js");
|
|
1866
2053
|
const confirmed = await confirm2(
|
|
1867
2054
|
`Disconnect source provider from "${app.name}"?`,
|
|
1868
2055
|
false,
|
|
@@ -2880,7 +3067,7 @@ function formatTime(ts) {
|
|
|
2880
3067
|
}
|
|
2881
3068
|
|
|
2882
3069
|
// src/commands/auth.ts
|
|
2883
|
-
import
|
|
3070
|
+
import open3 from "open";
|
|
2884
3071
|
|
|
2885
3072
|
// src/lib/auth-server.ts
|
|
2886
3073
|
import { randomBytes, timingSafeEqual } from "crypto";
|
|
@@ -2898,7 +3085,7 @@ function escapeHtml(value) {
|
|
|
2898
3085
|
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2899
3086
|
}
|
|
2900
3087
|
function startAuthServer(options = {}) {
|
|
2901
|
-
return new Promise((
|
|
3088
|
+
return new Promise((resolve4) => {
|
|
2902
3089
|
const app = express();
|
|
2903
3090
|
const expectedState = options.state ?? createAuthState();
|
|
2904
3091
|
let server;
|
|
@@ -3022,7 +3209,7 @@ function startAuthServer(options = {}) {
|
|
|
3022
3209
|
server = app.listen(0, "127.0.0.1", () => {
|
|
3023
3210
|
const address = server.address();
|
|
3024
3211
|
const port = typeof address === "object" && address ? address.port : 0;
|
|
3025
|
-
|
|
3212
|
+
resolve4({
|
|
3026
3213
|
port,
|
|
3027
3214
|
state: expectedState,
|
|
3028
3215
|
waitForCallback: () => callbackPromise,
|
|
@@ -3032,6 +3219,28 @@ function startAuthServer(options = {}) {
|
|
|
3032
3219
|
});
|
|
3033
3220
|
}
|
|
3034
3221
|
|
|
3222
|
+
// src/lib/browser.ts
|
|
3223
|
+
import open2 from "open";
|
|
3224
|
+
function canLaunchBrowser() {
|
|
3225
|
+
if (process.platform === "darwin" || process.platform === "win32") {
|
|
3226
|
+
return true;
|
|
3227
|
+
}
|
|
3228
|
+
return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY);
|
|
3229
|
+
}
|
|
3230
|
+
function paymentBrowserOpener(opts) {
|
|
3231
|
+
if (opts?.noOpen || !canLaunchBrowser()) return void 0;
|
|
3232
|
+
return async (url) => {
|
|
3233
|
+
try {
|
|
3234
|
+
await open2(url);
|
|
3235
|
+
} catch {
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
function shouldAutoConfirmPaidCheckout(amountDueHalalas) {
|
|
3240
|
+
const isPaidCheckout = typeof amountDueHalalas === "number" && amountDueHalalas > 0;
|
|
3241
|
+
return !isJsonMode() && isNonInteractiveMode() && canLaunchBrowser() && isPaidCheckout;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3035
3244
|
// src/commands/auth.ts
|
|
3036
3245
|
function refuseBrowserAuthForAgent(action) {
|
|
3037
3246
|
const message = `tarout ${action} requires a browser. Agents should ask the user to run \`tarout token <api-token>\` or set TAROUT_TOKEN \u2014 generate one at https://tarout.sa/dashboard/settings/profile.`;
|
|
@@ -3070,7 +3279,7 @@ function registerAuthCommands(program2) {
|
|
|
3070
3279
|
return;
|
|
3071
3280
|
}
|
|
3072
3281
|
}
|
|
3073
|
-
if (isJsonMode() ||
|
|
3282
|
+
if (isJsonMode() || !canLaunchBrowser()) {
|
|
3074
3283
|
refuseBrowserAuthForAgent("login");
|
|
3075
3284
|
}
|
|
3076
3285
|
const apiUrl = options.apiUrl;
|
|
@@ -3079,7 +3288,12 @@ function registerAuthCommands(program2) {
|
|
|
3079
3288
|
const authServer = await startAuthServer();
|
|
3080
3289
|
const callbackUrl = `http://localhost:${authServer.port}/callback?state=${encodeURIComponent(authServer.state)}`;
|
|
3081
3290
|
const authUrl = `${apiUrl}/cli-authorize?callback=${encodeURIComponent(callbackUrl)}`;
|
|
3082
|
-
|
|
3291
|
+
try {
|
|
3292
|
+
await open3(authUrl);
|
|
3293
|
+
} catch {
|
|
3294
|
+
authServer.close();
|
|
3295
|
+
refuseBrowserAuthForAgent("login");
|
|
3296
|
+
}
|
|
3083
3297
|
const _spinner = startSpinner("Waiting for authentication...");
|
|
3084
3298
|
try {
|
|
3085
3299
|
const authData = await authServer.waitForCallback();
|
|
@@ -3179,7 +3393,7 @@ function registerAuthCommands(program2) {
|
|
|
3179
3393
|
return;
|
|
3180
3394
|
}
|
|
3181
3395
|
}
|
|
3182
|
-
if (isJsonMode() ||
|
|
3396
|
+
if (isJsonMode() || !canLaunchBrowser()) {
|
|
3183
3397
|
refuseBrowserAuthForAgent("register");
|
|
3184
3398
|
}
|
|
3185
3399
|
const apiUrl = options.apiUrl;
|
|
@@ -3188,7 +3402,12 @@ function registerAuthCommands(program2) {
|
|
|
3188
3402
|
const authServer = await startAuthServer();
|
|
3189
3403
|
const callbackUrl = `http://localhost:${authServer.port}/callback?state=${encodeURIComponent(authServer.state)}`;
|
|
3190
3404
|
const authUrl = `${apiUrl}/cli-authorize?action=register&callback=${encodeURIComponent(callbackUrl)}`;
|
|
3191
|
-
|
|
3405
|
+
try {
|
|
3406
|
+
await open3(authUrl);
|
|
3407
|
+
} catch {
|
|
3408
|
+
authServer.close();
|
|
3409
|
+
refuseBrowserAuthForAgent("register");
|
|
3410
|
+
}
|
|
3192
3411
|
const _spinner = startSpinner("Waiting for account creation...");
|
|
3193
3412
|
try {
|
|
3194
3413
|
const authData = await authServer.waitForCallback();
|
|
@@ -3303,7 +3522,7 @@ function registerAuthCommands(program2) {
|
|
|
3303
3522
|
() => input("Token name (e.g., ci-deploy):")
|
|
3304
3523
|
);
|
|
3305
3524
|
}
|
|
3306
|
-
const { getApiClient: getApiClient2 } = await import("./api-
|
|
3525
|
+
const { getApiClient: getApiClient2 } = await import("./api-YFXACHOB.js");
|
|
3307
3526
|
const client = getApiClient2();
|
|
3308
3527
|
const profile = getCurrentProfile();
|
|
3309
3528
|
let keyOrg = profile?.organizationId;
|
|
@@ -3680,7 +3899,7 @@ function registerBackupsCommands(program2) {
|
|
|
3680
3899
|
try {
|
|
3681
3900
|
if (!isLoggedIn()) throw new AuthError();
|
|
3682
3901
|
if (!shouldSkipConfirmation()) {
|
|
3683
|
-
const { confirm: confirmFn } = await import("./prompts-
|
|
3902
|
+
const { confirm: confirmFn } = await import("./prompts-D72VJYWE.js");
|
|
3684
3903
|
const ok = await confirmFn(
|
|
3685
3904
|
`Restore backup file "${backupFile}"? This will overwrite current data.`,
|
|
3686
3905
|
false
|
|
@@ -3729,9 +3948,9 @@ function formatBytes2(bytes) {
|
|
|
3729
3948
|
|
|
3730
3949
|
// src/commands/billing.ts
|
|
3731
3950
|
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
3732
|
-
import open3 from "open";
|
|
3733
3951
|
|
|
3734
3952
|
// src/lib/billing-upgrade.ts
|
|
3953
|
+
var AGENT_BILLING_PERMISSION_HINT = `If your agent's permission system blocks this billing command, ask the user to allowlist Tarout billing once so you can run it directly (Claude Code: add "Bash(tarout billing:*)" to .claude/settings.json). Running an upgrade only opens the hosted payment page \u2014 the user still completes payment in the browser.`;
|
|
3735
3954
|
function resolveTarget(input2) {
|
|
3736
3955
|
if (input2.kind === "plan") return input2.planKey ?? "";
|
|
3737
3956
|
if (input2.kind === "addon") {
|
|
@@ -3797,6 +4016,13 @@ async function finalizeBillingMutation(client, result, ctx) {
|
|
|
3797
4016
|
}
|
|
3798
4017
|
const orderId = result.orderId;
|
|
3799
4018
|
const paymentUrl = result.paymentUrl;
|
|
4019
|
+
if (ctx.wait) ctx.onCheckoutOpened?.({ orderId, paymentUrl });
|
|
4020
|
+
if (ctx.openBrowser) {
|
|
4021
|
+
try {
|
|
4022
|
+
await ctx.openBrowser(paymentUrl);
|
|
4023
|
+
} catch {
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
3800
4026
|
if (!ctx.wait) {
|
|
3801
4027
|
return {
|
|
3802
4028
|
status: "payment_required",
|
|
@@ -3807,13 +4033,6 @@ async function finalizeBillingMutation(client, result, ctx) {
|
|
|
3807
4033
|
amountHalalas
|
|
3808
4034
|
};
|
|
3809
4035
|
}
|
|
3810
|
-
ctx.onCheckoutOpened?.({ orderId, paymentUrl });
|
|
3811
|
-
if (ctx.openBrowser) {
|
|
3812
|
-
try {
|
|
3813
|
-
await ctx.openBrowser(paymentUrl);
|
|
3814
|
-
} catch {
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
4036
|
const final = await pollCheckoutUntilTerminal(client, orderId, {
|
|
3818
4037
|
timeoutMs: ctx.timeoutMs ?? 6e5,
|
|
3819
4038
|
intervalMs: 4e3
|
|
@@ -3994,12 +4213,6 @@ function reportBillingResult(result, label) {
|
|
|
3994
4213
|
const code = emitBillingResult(result, { label });
|
|
3995
4214
|
if (code !== ExitCode.SUCCESS) exit(code);
|
|
3996
4215
|
}
|
|
3997
|
-
function browserOpener(noOpen) {
|
|
3998
|
-
if (isJsonMode() || noOpen) return void 0;
|
|
3999
|
-
return async (url) => {
|
|
4000
|
-
await open3(url);
|
|
4001
|
-
};
|
|
4002
|
-
}
|
|
4003
4216
|
function isConflictError(err) {
|
|
4004
4217
|
const e = err;
|
|
4005
4218
|
return (e?.code ?? e?.data?.code) === "CONFLICT";
|
|
@@ -4205,24 +4418,30 @@ function registerBillingCommands(program2) {
|
|
|
4205
4418
|
);
|
|
4206
4419
|
}
|
|
4207
4420
|
log("");
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4421
|
+
if (shouldAutoConfirmPaidCheckout(amountDueHalalas)) {
|
|
4422
|
+
log(
|
|
4423
|
+
"Opening the secure payment page in your browser to complete the upgrade..."
|
|
4424
|
+
);
|
|
4425
|
+
} else {
|
|
4426
|
+
const confirmed = await confirm(
|
|
4427
|
+
`Switch to plan "${targetPlan}"?`,
|
|
4428
|
+
false,
|
|
4429
|
+
{
|
|
4430
|
+
field: "confirm_upgrade",
|
|
4431
|
+
flag: "--yes",
|
|
4432
|
+
context: {
|
|
4433
|
+
plan: targetPlan,
|
|
4434
|
+
quantity: planQuantity,
|
|
4435
|
+
billingPeriod,
|
|
4436
|
+
addons,
|
|
4437
|
+
amountDueHalalas
|
|
4438
|
+
}
|
|
4220
4439
|
}
|
|
4440
|
+
);
|
|
4441
|
+
if (!confirmed) {
|
|
4442
|
+
log("Cancelled.");
|
|
4443
|
+
return;
|
|
4221
4444
|
}
|
|
4222
|
-
);
|
|
4223
|
-
if (!confirmed) {
|
|
4224
|
-
log("Cancelled.");
|
|
4225
|
-
return;
|
|
4226
4445
|
}
|
|
4227
4446
|
}
|
|
4228
4447
|
const _changeSpinner = startSpinner("Changing plan...");
|
|
@@ -4234,7 +4453,7 @@ function registerBillingCommands(program2) {
|
|
|
4234
4453
|
addons,
|
|
4235
4454
|
wait: options.wait,
|
|
4236
4455
|
timeoutMs: options.timeout * 1e3,
|
|
4237
|
-
openBrowser:
|
|
4456
|
+
openBrowser: paymentBrowserOpener({ noOpen: options.open === false }),
|
|
4238
4457
|
onCheckoutOpened: ({ orderId, paymentUrl }) => {
|
|
4239
4458
|
if (isJsonMode()) {
|
|
4240
4459
|
outputJsonLine({
|
|
@@ -4438,7 +4657,7 @@ function registerBillingCommands(program2) {
|
|
|
4438
4657
|
target: addonKey,
|
|
4439
4658
|
wait: options.wait,
|
|
4440
4659
|
timeoutMs: options.timeout * 1e3,
|
|
4441
|
-
openBrowser:
|
|
4660
|
+
openBrowser: paymentBrowserOpener({ noOpen: options.open === false })
|
|
4442
4661
|
});
|
|
4443
4662
|
reportBillingResult(result, `Addon: ${addonKey} \xD7${quantity}`);
|
|
4444
4663
|
} catch (err) {
|
|
@@ -4492,7 +4711,7 @@ function registerBillingCommands(program2) {
|
|
|
4492
4711
|
quantity,
|
|
4493
4712
|
wait: options.wait,
|
|
4494
4713
|
timeoutMs: options.timeout * 1e3,
|
|
4495
|
-
openBrowser:
|
|
4714
|
+
openBrowser: paymentBrowserOpener({ noOpen: options.open === false })
|
|
4496
4715
|
});
|
|
4497
4716
|
succeedSpinner("Plan quantity processed.");
|
|
4498
4717
|
reportBillingResult(result, `Plan quantity: ${quantity}`);
|
|
@@ -4581,7 +4800,7 @@ Purchase ${quantity}\xD7 ${colors.cyan(addonKey)}?`);
|
|
|
4581
4800
|
quantity,
|
|
4582
4801
|
wait: options.wait,
|
|
4583
4802
|
timeoutMs: options.timeout * 1e3,
|
|
4584
|
-
openBrowser:
|
|
4803
|
+
openBrowser: paymentBrowserOpener({ noOpen: options.open === false })
|
|
4585
4804
|
});
|
|
4586
4805
|
succeedSpinner("Addon purchase processed.");
|
|
4587
4806
|
reportBillingResult(result, `Addon: ${addonKey} \xD7${quantity}`);
|
|
@@ -4863,16 +5082,16 @@ function formatDate4(date) {
|
|
|
4863
5082
|
|
|
4864
5083
|
// src/lib/process.ts
|
|
4865
5084
|
import { spawn } from "child_process";
|
|
4866
|
-
import { existsSync, readFileSync } from "fs";
|
|
4867
|
-
import { join } from "path";
|
|
5085
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
5086
|
+
import { join as join2 } from "path";
|
|
4868
5087
|
function readPackageJson(basePath) {
|
|
4869
5088
|
const base = basePath || process.cwd();
|
|
4870
|
-
const packagePath =
|
|
4871
|
-
if (!
|
|
5089
|
+
const packagePath = join2(base, "package.json");
|
|
5090
|
+
if (!existsSync2(packagePath)) {
|
|
4872
5091
|
return null;
|
|
4873
5092
|
}
|
|
4874
5093
|
try {
|
|
4875
|
-
const content =
|
|
5094
|
+
const content = readFileSync2(packagePath, "utf-8");
|
|
4876
5095
|
return JSON.parse(content);
|
|
4877
5096
|
} catch {
|
|
4878
5097
|
return null;
|
|
@@ -4880,16 +5099,16 @@ function readPackageJson(basePath) {
|
|
|
4880
5099
|
}
|
|
4881
5100
|
function detectPackageManager(basePath) {
|
|
4882
5101
|
const base = basePath || process.cwd();
|
|
4883
|
-
if (
|
|
5102
|
+
if (existsSync2(join2(base, "bun.lockb")) || existsSync2(join2(base, "bun.lock"))) {
|
|
4884
5103
|
return "bun";
|
|
4885
5104
|
}
|
|
4886
|
-
if (
|
|
5105
|
+
if (existsSync2(join2(base, "pnpm-lock.yaml"))) {
|
|
4887
5106
|
return "pnpm";
|
|
4888
5107
|
}
|
|
4889
|
-
if (
|
|
5108
|
+
if (existsSync2(join2(base, "yarn.lock"))) {
|
|
4890
5109
|
return "yarn";
|
|
4891
5110
|
}
|
|
4892
|
-
if (
|
|
5111
|
+
if (existsSync2(join2(base, "package-lock.json"))) {
|
|
4893
5112
|
return "npm";
|
|
4894
5113
|
}
|
|
4895
5114
|
const pkg = readPackageJson(basePath);
|
|
@@ -5044,7 +5263,7 @@ function getDefaultPort(pkg) {
|
|
|
5044
5263
|
return framework?.defaultPort || 3e3;
|
|
5045
5264
|
}
|
|
5046
5265
|
function runCommand(command, env, options = {}) {
|
|
5047
|
-
return new Promise((
|
|
5266
|
+
return new Promise((resolve4) => {
|
|
5048
5267
|
const [cmd, ...args] = parseCommand(command);
|
|
5049
5268
|
const spawnOptions = {
|
|
5050
5269
|
cwd: options.cwd || process.cwd(),
|
|
@@ -5079,7 +5298,7 @@ function runCommand(command, env, options = {}) {
|
|
|
5079
5298
|
child.on("close", (code, signal) => {
|
|
5080
5299
|
process.off("SIGINT", handleSignal);
|
|
5081
5300
|
process.off("SIGTERM", handleSignal);
|
|
5082
|
-
|
|
5301
|
+
resolve4({
|
|
5083
5302
|
exitCode: code ?? 1,
|
|
5084
5303
|
signal
|
|
5085
5304
|
});
|
|
@@ -5088,7 +5307,7 @@ function runCommand(command, env, options = {}) {
|
|
|
5088
5307
|
process.off("SIGINT", handleSignal);
|
|
5089
5308
|
process.off("SIGTERM", handleSignal);
|
|
5090
5309
|
console.error(`Failed to start process: ${err.message}`);
|
|
5091
|
-
|
|
5310
|
+
resolve4({
|
|
5092
5311
|
exitCode: 1,
|
|
5093
5312
|
signal: null
|
|
5094
5313
|
});
|
|
@@ -5277,32 +5496,32 @@ function findApp2(apps, identifier) {
|
|
|
5277
5496
|
}
|
|
5278
5497
|
|
|
5279
5498
|
// src/commands/call.ts
|
|
5280
|
-
import { existsSync as
|
|
5499
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
5281
5500
|
import { homedir } from "os";
|
|
5282
|
-
import { join as
|
|
5501
|
+
import { join as join3 } from "path";
|
|
5283
5502
|
var MANIFEST_TTL_MS = 5 * 60 * 1e3;
|
|
5284
5503
|
function manifestCachePath() {
|
|
5285
|
-
return
|
|
5504
|
+
return join3(homedir(), ".tarout", "surface-manifest-cache.json");
|
|
5286
5505
|
}
|
|
5287
5506
|
async function fetchManifestFresh(client, apiUrl) {
|
|
5288
5507
|
const manifest = await client.settings.getSurfaceManifest.query();
|
|
5289
5508
|
try {
|
|
5290
|
-
const dir =
|
|
5291
|
-
if (!
|
|
5509
|
+
const dir = join3(homedir(), ".tarout");
|
|
5510
|
+
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
|
|
5292
5511
|
let cache = {};
|
|
5293
5512
|
try {
|
|
5294
|
-
cache = JSON.parse(
|
|
5513
|
+
cache = JSON.parse(readFileSync3(manifestCachePath(), "utf8"));
|
|
5295
5514
|
} catch {
|
|
5296
5515
|
}
|
|
5297
5516
|
cache[apiUrl] = { at: Date.now(), manifest };
|
|
5298
|
-
|
|
5517
|
+
writeFileSync2(manifestCachePath(), JSON.stringify(cache), { mode: 384 });
|
|
5299
5518
|
} catch {
|
|
5300
5519
|
}
|
|
5301
5520
|
return manifest;
|
|
5302
5521
|
}
|
|
5303
5522
|
async function loadManifest(client, apiUrl) {
|
|
5304
5523
|
try {
|
|
5305
|
-
const cache = JSON.parse(
|
|
5524
|
+
const cache = JSON.parse(readFileSync3(manifestCachePath(), "utf8"));
|
|
5306
5525
|
const entry = cache[apiUrl];
|
|
5307
5526
|
if (entry && Date.now() - entry.at < MANIFEST_TTL_MS && Array.isArray(entry.manifest)) {
|
|
5308
5527
|
return entry.manifest;
|
|
@@ -5362,7 +5581,7 @@ function registerCallCommand(program2) {
|
|
|
5362
5581
|
);
|
|
5363
5582
|
}
|
|
5364
5583
|
let input2 = {};
|
|
5365
|
-
const rawInput = opts.inputFile ?
|
|
5584
|
+
const rawInput = opts.inputFile ? readFileSync3(opts.inputFile, "utf8") : opts.input;
|
|
5366
5585
|
if (rawInput && rawInput.trim()) {
|
|
5367
5586
|
try {
|
|
5368
5587
|
input2 = JSON.parse(rawInput);
|
|
@@ -5493,15 +5712,15 @@ import { spawn as spawn2 } from "child_process";
|
|
|
5493
5712
|
import { execFile } from "child_process";
|
|
5494
5713
|
import {
|
|
5495
5714
|
createReadStream,
|
|
5496
|
-
existsSync as
|
|
5715
|
+
existsSync as existsSync4,
|
|
5497
5716
|
mkdtempSync,
|
|
5498
5717
|
readdirSync,
|
|
5499
|
-
readFileSync as
|
|
5718
|
+
readFileSync as readFileSync4,
|
|
5500
5719
|
rmSync,
|
|
5501
5720
|
statSync
|
|
5502
5721
|
} from "fs";
|
|
5503
5722
|
import { tmpdir } from "os";
|
|
5504
|
-
import { basename, dirname, join as
|
|
5723
|
+
import { basename, dirname, join as join4 } from "path";
|
|
5505
5724
|
import { promisify } from "util";
|
|
5506
5725
|
import open4 from "open";
|
|
5507
5726
|
|
|
@@ -5603,13 +5822,13 @@ function streamDeploymentLogs(deploymentId, options) {
|
|
|
5603
5822
|
}
|
|
5604
5823
|
});
|
|
5605
5824
|
let buffer = "";
|
|
5606
|
-
const done = new Promise((
|
|
5825
|
+
const done = new Promise((resolve4) => {
|
|
5607
5826
|
ws.on("close", (code, reason) => {
|
|
5608
5827
|
if (buffer.length > 0) {
|
|
5609
5828
|
options.onData(buffer);
|
|
5610
5829
|
}
|
|
5611
5830
|
options.onClose?.(code, reason.toString());
|
|
5612
|
-
|
|
5831
|
+
resolve4({ code, reason: reason.toString() });
|
|
5613
5832
|
});
|
|
5614
5833
|
});
|
|
5615
5834
|
ws.on("open", () => {
|
|
@@ -5868,7 +6087,7 @@ async function confirmRegion(region) {
|
|
|
5868
6087
|
}
|
|
5869
6088
|
}
|
|
5870
6089
|
function inspectCurrentProject(cwd = process.cwd()) {
|
|
5871
|
-
const packageJson = readJsonFile(
|
|
6090
|
+
const packageJson = readJsonFile(join4(cwd, "package.json"));
|
|
5872
6091
|
const dependencies = new Set(
|
|
5873
6092
|
Object.keys({
|
|
5874
6093
|
...packageJson?.dependencies ?? {},
|
|
@@ -5916,8 +6135,8 @@ function printProjectInspection(inspection) {
|
|
|
5916
6135
|
}
|
|
5917
6136
|
function readJsonFile(path) {
|
|
5918
6137
|
try {
|
|
5919
|
-
if (!
|
|
5920
|
-
return JSON.parse(
|
|
6138
|
+
if (!existsSync4(path)) return null;
|
|
6139
|
+
return JSON.parse(readFileSync4(path, "utf8"));
|
|
5921
6140
|
} catch {
|
|
5922
6141
|
return null;
|
|
5923
6142
|
}
|
|
@@ -5926,7 +6145,7 @@ function readTextFile(path, maxBytes) {
|
|
|
5926
6145
|
try {
|
|
5927
6146
|
const stat = statSync(path);
|
|
5928
6147
|
if (stat.size > maxBytes) return "";
|
|
5929
|
-
return
|
|
6148
|
+
return readFileSync4(path, "utf8");
|
|
5930
6149
|
} catch {
|
|
5931
6150
|
return "";
|
|
5932
6151
|
}
|
|
@@ -5981,7 +6200,7 @@ function collectInspectableFiles(root) {
|
|
|
5981
6200
|
}
|
|
5982
6201
|
for (const entry of entries) {
|
|
5983
6202
|
if (files.length >= 400) return;
|
|
5984
|
-
const fullPath =
|
|
6203
|
+
const fullPath = join4(dir, entry.name);
|
|
5985
6204
|
if (entry.isDirectory()) {
|
|
5986
6205
|
if (!excludedDirs.has(entry.name)) walk(fullPath, depth + 1);
|
|
5987
6206
|
continue;
|
|
@@ -6033,7 +6252,7 @@ function detectDatabase(dependencies, files, cwd) {
|
|
|
6033
6252
|
}
|
|
6034
6253
|
}
|
|
6035
6254
|
const prismaSchema = readTextFile(
|
|
6036
|
-
|
|
6255
|
+
join4(cwd, "prisma", "schema.prisma"),
|
|
6037
6256
|
256 * 1024
|
|
6038
6257
|
);
|
|
6039
6258
|
if (/provider\s*=\s*"postgresql"/i.test(prismaSchema)) {
|
|
@@ -6114,15 +6333,15 @@ function detectStorage(dependencies, files) {
|
|
|
6114
6333
|
return { detected: reasons.length > 0, reasons: uniqueReasons(reasons) };
|
|
6115
6334
|
}
|
|
6116
6335
|
function detectGitSource(cwd) {
|
|
6117
|
-
const gitPath =
|
|
6118
|
-
if (!
|
|
6119
|
-
let configPath =
|
|
6336
|
+
const gitPath = join4(cwd, ".git");
|
|
6337
|
+
if (!existsSync4(gitPath)) return { hasGit: false };
|
|
6338
|
+
let configPath = join4(gitPath, "config");
|
|
6120
6339
|
try {
|
|
6121
6340
|
const stat = statSync(gitPath);
|
|
6122
6341
|
if (stat.isFile()) {
|
|
6123
6342
|
const content = readTextFile(gitPath, 4096);
|
|
6124
6343
|
const match = content.match(/gitdir:\s*(.+)/i);
|
|
6125
|
-
if (match?.[1]) configPath =
|
|
6344
|
+
if (match?.[1]) configPath = join4(cwd, match[1].trim(), "config");
|
|
6126
6345
|
}
|
|
6127
6346
|
} catch {
|
|
6128
6347
|
}
|
|
@@ -6168,7 +6387,12 @@ async function resolveDeploymentTarget(client, profile, appIdentifier, options =
|
|
|
6168
6387
|
}
|
|
6169
6388
|
const linkedProject = getProjectConfig();
|
|
6170
6389
|
const linkedApp = linkedProject ? findApp3(apps, linkedProject.applicationId) ?? findApp3(apps, linkedProject.name) : void 0;
|
|
6171
|
-
if (
|
|
6390
|
+
if (linkedApp) {
|
|
6391
|
+
return reuseAppTarget(client, profile, linkedApp, {
|
|
6392
|
+
promptForSource: false
|
|
6393
|
+
});
|
|
6394
|
+
}
|
|
6395
|
+
if (linkedProject && !isJsonMode()) {
|
|
6172
6396
|
log("");
|
|
6173
6397
|
log(
|
|
6174
6398
|
colors.warn(
|
|
@@ -6181,12 +6405,10 @@ async function resolveDeploymentTarget(client, profile, appIdentifier, options =
|
|
|
6181
6405
|
return createNewAppTarget(client, profile, options);
|
|
6182
6406
|
}
|
|
6183
6407
|
if (shouldSkipConfirmation()) {
|
|
6184
|
-
if (linkedApp) return reuseAppTarget(client, profile, linkedApp);
|
|
6185
6408
|
assertConfiguredSourceAllowsCreate(sourcePreference, true);
|
|
6186
6409
|
return createNewAppTarget(client, profile, options);
|
|
6187
6410
|
}
|
|
6188
6411
|
const createValue = "__create__";
|
|
6189
|
-
const orderedApps = linkedApp ? [linkedApp, ...apps.filter((a) => a.applicationId !== linkedApp.applicationId)] : apps;
|
|
6190
6412
|
const selected = await select(
|
|
6191
6413
|
"Create a new app or reuse an existing one?",
|
|
6192
6414
|
[
|
|
@@ -6194,17 +6416,16 @@ async function resolveDeploymentTarget(client, profile, appIdentifier, options =
|
|
|
6194
6416
|
name: `Create a new app from ${colors.cyan(basename(process.cwd()) || "this directory")}`,
|
|
6195
6417
|
value: createValue
|
|
6196
6418
|
},
|
|
6197
|
-
...
|
|
6198
|
-
name: `Reuse ${app2.name} ${colors.dim(`(${app2.applicationId.slice(0, 8)})`)}
|
|
6419
|
+
...apps.map((app2) => ({
|
|
6420
|
+
name: `Reuse ${app2.name} ${colors.dim(`(${app2.applicationId.slice(0, 8)})`)}`,
|
|
6199
6421
|
value: app2.applicationId
|
|
6200
6422
|
}))
|
|
6201
6423
|
],
|
|
6202
6424
|
{
|
|
6203
6425
|
field: "deploy_app",
|
|
6204
|
-
flag: "--new-app to create a new app, or
|
|
6426
|
+
flag: "--new-app to create a new app, or pass the app id or name as the positional argument (tarout deploy <id|name>) to reuse an existing one",
|
|
6205
6427
|
context: {
|
|
6206
|
-
|
|
6207
|
-
apps: orderedApps.map((a) => ({
|
|
6428
|
+
apps: apps.map((a) => ({
|
|
6208
6429
|
id: a.applicationId,
|
|
6209
6430
|
name: a.name
|
|
6210
6431
|
}))
|
|
@@ -6236,7 +6457,7 @@ async function createNewAppTarget(client, profile, options) {
|
|
|
6236
6457
|
shouldUploadSource: true
|
|
6237
6458
|
};
|
|
6238
6459
|
}
|
|
6239
|
-
async function reuseAppTarget(client, profile, app) {
|
|
6460
|
+
async function reuseAppTarget(client, profile, app, opts = {}) {
|
|
6240
6461
|
setProjectConfig({
|
|
6241
6462
|
applicationId: app.applicationId,
|
|
6242
6463
|
name: app.name,
|
|
@@ -6248,7 +6469,11 @@ async function reuseAppTarget(client, profile, app) {
|
|
|
6248
6469
|
app,
|
|
6249
6470
|
createdApp: false,
|
|
6250
6471
|
hasConfiguredSource: hasConfiguredSource(details),
|
|
6251
|
-
|
|
6472
|
+
// A deterministic redeploy (linked app) must not prompt for a source
|
|
6473
|
+
// override — it silently reuses the app's existing source, or the current
|
|
6474
|
+
// folder when `--source upload` is set downstream. Only an interactive
|
|
6475
|
+
// menu reuse offers the override.
|
|
6476
|
+
shouldUploadSource: opts.promptForSource === false ? shouldUseLocalSource(details, { linkedProject: true }) : await promptForLocalSourceIfNeeded(details)
|
|
6252
6477
|
};
|
|
6253
6478
|
}
|
|
6254
6479
|
async function createAppFromCurrentDirectory(client, profile, options = {}) {
|
|
@@ -6400,9 +6625,7 @@ async function runInlineUpgrade(client, planKey) {
|
|
|
6400
6625
|
planKey,
|
|
6401
6626
|
wait: true,
|
|
6402
6627
|
timeoutMs: 6e5,
|
|
6403
|
-
openBrowser:
|
|
6404
|
-
await open4(url);
|
|
6405
|
-
},
|
|
6628
|
+
openBrowser: paymentBrowserOpener(),
|
|
6406
6629
|
onCheckoutOpened: ({ orderId, paymentUrl }) => {
|
|
6407
6630
|
log("");
|
|
6408
6631
|
log("Open this URL to complete payment:");
|
|
@@ -6452,9 +6675,7 @@ async function runInlineTargetedRemedy(client, remedy) {
|
|
|
6452
6675
|
...input2,
|
|
6453
6676
|
wait: true,
|
|
6454
6677
|
timeoutMs: 6e5,
|
|
6455
|
-
openBrowser:
|
|
6456
|
-
await open4(url);
|
|
6457
|
-
},
|
|
6678
|
+
openBrowser: paymentBrowserOpener(),
|
|
6458
6679
|
onCheckoutOpened: ({ orderId, paymentUrl }) => {
|
|
6459
6680
|
log("");
|
|
6460
6681
|
log("Open this URL to complete payment:");
|
|
@@ -6651,7 +6872,8 @@ async function emitNeedsUpgrade(client, err, requestedPlan, retryCommand) {
|
|
|
6651
6872
|
suggestedTarget: remedy.targetKey,
|
|
6652
6873
|
nextCommand: remedy.command,
|
|
6653
6874
|
options,
|
|
6654
|
-
hint
|
|
6875
|
+
hint,
|
|
6876
|
+
permissionHint: AGENT_BILLING_PERMISSION_HINT
|
|
6655
6877
|
});
|
|
6656
6878
|
}
|
|
6657
6879
|
async function listFreeDatabasesSafely(client) {
|
|
@@ -7725,8 +7947,8 @@ async function uploadCurrentDirectorySource(client, applicationId, appName) {
|
|
|
7725
7947
|
}
|
|
7726
7948
|
}
|
|
7727
7949
|
async function createSourceArchive() {
|
|
7728
|
-
const tempDir = mkdtempSync(
|
|
7729
|
-
const archivePath =
|
|
7950
|
+
const tempDir = mkdtempSync(join4(tmpdir(), "tarout-source-"));
|
|
7951
|
+
const archivePath = join4(tempDir, "source.zip");
|
|
7730
7952
|
const excludes = [
|
|
7731
7953
|
".git/*",
|
|
7732
7954
|
".tarout/*",
|
|
@@ -8231,7 +8453,7 @@ function registerDeployCommands(program2) {
|
|
|
8231
8453
|
name: `${colors.cyan(d.deploymentId.slice(0, 8))} - ${d.title || "Deployment"} (${formatDate5(d.createdAt)})${index === 0 ? colors.dim(" [current]") : ""}`,
|
|
8232
8454
|
value: d.deploymentId
|
|
8233
8455
|
}));
|
|
8234
|
-
const { select: select2 } = await import("./prompts-
|
|
8456
|
+
const { select: select2 } = await import("./prompts-D72VJYWE.js");
|
|
8235
8457
|
targetDeploymentId = await select2(
|
|
8236
8458
|
"Select deployment:",
|
|
8237
8459
|
choices,
|
|
@@ -8250,7 +8472,7 @@ function registerDeployCommands(program2) {
|
|
|
8250
8472
|
` Created: ${targetDeployment ? formatDate5(targetDeployment.createdAt) : colors.dim("unknown")}`
|
|
8251
8473
|
);
|
|
8252
8474
|
log("");
|
|
8253
|
-
const { confirm: confirm2 } = await import("./prompts-
|
|
8475
|
+
const { confirm: confirm2 } = await import("./prompts-D72VJYWE.js");
|
|
8254
8476
|
const confirmed = await confirm2(
|
|
8255
8477
|
"Are you sure you want to rollback?",
|
|
8256
8478
|
false,
|
|
@@ -8382,7 +8604,7 @@ function formatDate5(date) {
|
|
|
8382
8604
|
});
|
|
8383
8605
|
}
|
|
8384
8606
|
function sleep(ms) {
|
|
8385
|
-
return new Promise((
|
|
8607
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
8386
8608
|
}
|
|
8387
8609
|
async function streamDeploymentWithLogs(client, deploymentId, appName, applicationId) {
|
|
8388
8610
|
stopSpinner();
|
|
@@ -11953,7 +12175,7 @@ function truncate(str, max) {
|
|
|
11953
12175
|
}
|
|
11954
12176
|
|
|
11955
12177
|
// src/commands/env.ts
|
|
11956
|
-
import { chmodSync, existsSync as
|
|
12178
|
+
import { chmodSync, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
11957
12179
|
function registerEnvCommands(program2) {
|
|
11958
12180
|
const env = program2.command("env").argument("<app>", "Application ID or name").description("Manage environment variables");
|
|
11959
12181
|
env.command("list").alias("ls").description("List all environment variables").option("--reveal", "Show actual values (not masked)").action(async (options, command) => {
|
|
@@ -12113,7 +12335,7 @@ function registerEnvCommands(program2) {
|
|
|
12113
12335
|
);
|
|
12114
12336
|
throw new NotFoundError("Application", appIdentifier, suggestions);
|
|
12115
12337
|
}
|
|
12116
|
-
if (
|
|
12338
|
+
if (existsSync5(options.output) && !shouldSkipConfirmation()) {
|
|
12117
12339
|
succeedSpinner();
|
|
12118
12340
|
const confirmed = await confirm(
|
|
12119
12341
|
`File ${options.output} already exists. Overwrite?`,
|
|
@@ -12134,7 +12356,7 @@ function registerEnvCommands(program2) {
|
|
|
12134
12356
|
format: "dotenv",
|
|
12135
12357
|
maskSecrets: !options.reveal
|
|
12136
12358
|
});
|
|
12137
|
-
|
|
12359
|
+
writeFileSync3(options.output, result.content, { mode: 384 });
|
|
12138
12360
|
try {
|
|
12139
12361
|
chmodSync(options.output, 384);
|
|
12140
12362
|
} catch {
|
|
@@ -12153,10 +12375,10 @@ function registerEnvCommands(program2) {
|
|
|
12153
12375
|
try {
|
|
12154
12376
|
if (!isLoggedIn()) throw new AuthError();
|
|
12155
12377
|
const appIdentifier = command.parent.parent.args[0];
|
|
12156
|
-
if (!
|
|
12378
|
+
if (!existsSync5(options.input)) {
|
|
12157
12379
|
throw new InvalidArgumentError(`File not found: ${options.input}`);
|
|
12158
12380
|
}
|
|
12159
|
-
const content =
|
|
12381
|
+
const content = readFileSync5(options.input, "utf-8");
|
|
12160
12382
|
const client = getApiClient();
|
|
12161
12383
|
const _spinner = startSpinner("Uploading environment variables...");
|
|
12162
12384
|
const apps = await client.application.allByOrganization.query();
|
|
@@ -12380,7 +12602,7 @@ ${colors.bold(key)}: ${maskValue(val.value || String(v))}
|
|
|
12380
12602
|
} else {
|
|
12381
12603
|
const _raw = await import("process");
|
|
12382
12604
|
log('Enter JSON key-value object (e.g. {"KEY":"value"}):');
|
|
12383
|
-
const input2 = await (await import("./prompts-
|
|
12605
|
+
const input2 = await (await import("./prompts-D72VJYWE.js")).input(
|
|
12384
12606
|
"JSON:"
|
|
12385
12607
|
);
|
|
12386
12608
|
vars = JSON.parse(input2);
|
|
@@ -12403,7 +12625,7 @@ ${colors.bold(key)}: ${maskValue(val.value || String(v))}
|
|
|
12403
12625
|
try {
|
|
12404
12626
|
if (!isLoggedIn()) throw new AuthError();
|
|
12405
12627
|
if (!shouldSkipConfirmation()) {
|
|
12406
|
-
const { confirm: confirmFn } = await import("./prompts-
|
|
12628
|
+
const { confirm: confirmFn } = await import("./prompts-D72VJYWE.js");
|
|
12407
12629
|
const ok = await confirmFn(
|
|
12408
12630
|
`Delete ${keys.length} variable(s)?`,
|
|
12409
12631
|
false
|
|
@@ -12442,7 +12664,7 @@ ${colors.bold(key)}: ${maskValue(val.value || String(v))}
|
|
|
12442
12664
|
);
|
|
12443
12665
|
if (!app) throw new NotFoundError("Application", appIdentifier);
|
|
12444
12666
|
if (!shouldSkipConfirmation()) {
|
|
12445
|
-
const { confirm: confirmFn } = await import("./prompts-
|
|
12667
|
+
const { confirm: confirmFn } = await import("./prompts-D72VJYWE.js");
|
|
12446
12668
|
const ok = await confirmFn(
|
|
12447
12669
|
`Copy env vars from ${fromEnvId} to ${toEnvId}?`,
|
|
12448
12670
|
false
|
|
@@ -12890,8 +13112,8 @@ function registerInboxCommands(program2) {
|
|
|
12890
13112
|
}
|
|
12891
13113
|
|
|
12892
13114
|
// src/commands/init.ts
|
|
12893
|
-
import { existsSync as
|
|
12894
|
-
import { basename as basename2, join as
|
|
13115
|
+
import { existsSync as existsSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
13116
|
+
import { basename as basename2, join as join5, resolve as resolve2 } from "path";
|
|
12895
13117
|
var DEFAULT_REGION2 = "me-central2";
|
|
12896
13118
|
var STARTER_INDEX_JS = `import { createServer } from "node:http";
|
|
12897
13119
|
|
|
@@ -12909,8 +13131,8 @@ function toPackageName(name) {
|
|
|
12909
13131
|
}
|
|
12910
13132
|
function scaffoldStarter(cwd) {
|
|
12911
13133
|
const written = [];
|
|
12912
|
-
const pkgPath =
|
|
12913
|
-
if (
|
|
13134
|
+
const pkgPath = join5(cwd, "package.json");
|
|
13135
|
+
if (existsSync6(pkgPath)) return written;
|
|
12914
13136
|
const pkg = {
|
|
12915
13137
|
name: toPackageName(basename2(cwd) || "tarout-app"),
|
|
12916
13138
|
version: "0.1.0",
|
|
@@ -12918,12 +13140,12 @@ function scaffoldStarter(cwd) {
|
|
|
12918
13140
|
type: "module",
|
|
12919
13141
|
scripts: { start: "node index.js", dev: "node index.js" }
|
|
12920
13142
|
};
|
|
12921
|
-
|
|
13143
|
+
writeFileSync4(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
12922
13144
|
`);
|
|
12923
13145
|
written.push("package.json");
|
|
12924
|
-
const indexPath =
|
|
12925
|
-
if (!
|
|
12926
|
-
|
|
13146
|
+
const indexPath = join5(cwd, "index.js");
|
|
13147
|
+
if (!existsSync6(indexPath)) {
|
|
13148
|
+
writeFileSync4(indexPath, STARTER_INDEX_JS);
|
|
12927
13149
|
written.push("index.js");
|
|
12928
13150
|
}
|
|
12929
13151
|
return written;
|
|
@@ -12935,9 +13157,9 @@ function writeEnvFile(cwd, vars) {
|
|
|
12935
13157
|
const lines = Object.entries(vars).map(
|
|
12936
13158
|
([k, v]) => `${k}=${envValueNeedsQuote(v) ? JSON.stringify(v) : v}`
|
|
12937
13159
|
);
|
|
12938
|
-
const primary =
|
|
12939
|
-
const target =
|
|
12940
|
-
|
|
13160
|
+
const primary = join5(cwd, ".env");
|
|
13161
|
+
const target = existsSync6(primary) ? join5(cwd, ".env.tarout") : primary;
|
|
13162
|
+
writeFileSync4(target, `${lines.join("\n")}
|
|
12941
13163
|
`, { mode: 384 });
|
|
12942
13164
|
return target;
|
|
12943
13165
|
}
|
|
@@ -12956,7 +13178,7 @@ function registerInitCommand(program2) {
|
|
|
12956
13178
|
"Provision a database: none, postgres, or mysql (defaults to auto-detected)"
|
|
12957
13179
|
).option("--database-plan <plan>", "Database plan (e.g. free, starter)").option("--storage", "Provision file storage").option("--storage-plan <plan>", "Storage plan (e.g. free, starter)").option("--scaffold", "Write a minimal starter app if the directory is empty").option("--no-env-write", "Do not write a local .env file with connection strings").action(async (cwdArg, options) => {
|
|
12958
13180
|
try {
|
|
12959
|
-
const cwd = cwdArg ?
|
|
13181
|
+
const cwd = cwdArg ? resolve2(cwdArg) : process.cwd();
|
|
12960
13182
|
if (cwdArg) process.chdir(cwd);
|
|
12961
13183
|
if (options.scaffold) {
|
|
12962
13184
|
const files = scaffoldStarter(cwd);
|
|
@@ -13019,7 +13241,8 @@ function registerInitCommand(program2) {
|
|
|
13019
13241
|
outputError("NEEDS_UPGRADE", message, {
|
|
13020
13242
|
suggestedPlan: inferSuggestedPlan(options.plan),
|
|
13021
13243
|
failedEntitlementKey: extractEntitlementKeyFromError(err),
|
|
13022
|
-
hint: "Run `tarout billing upgrade <plan> --wait` to add slots, then retry `tarout init`."
|
|
13244
|
+
hint: "Run `tarout billing upgrade <plan> --wait` to add slots, then retry `tarout init`.",
|
|
13245
|
+
permissionHint: AGENT_BILLING_PERMISSION_HINT
|
|
13023
13246
|
});
|
|
13024
13247
|
exit(ExitCode.PERMISSION_DENIED);
|
|
13025
13248
|
}
|
|
@@ -13156,8 +13379,8 @@ function registerKeysCommands(program2) {
|
|
|
13156
13379
|
});
|
|
13157
13380
|
}
|
|
13158
13381
|
if (!publicKey && options.file) {
|
|
13159
|
-
const { readFileSync:
|
|
13160
|
-
publicKey =
|
|
13382
|
+
const { readFileSync: readFileSync6 } = await import("fs");
|
|
13383
|
+
publicKey = readFileSync6(options.file, "utf-8").trim();
|
|
13161
13384
|
}
|
|
13162
13385
|
if (!publicKey) {
|
|
13163
13386
|
log(
|
|
@@ -19345,7 +19568,7 @@ function truncate3(str, max) {
|
|
|
19345
19568
|
}
|
|
19346
19569
|
|
|
19347
19570
|
// src/commands/up.ts
|
|
19348
|
-
import { resolve as
|
|
19571
|
+
import { resolve as resolve3 } from "path";
|
|
19349
19572
|
var DEFAULT_REGION3 = "me-central2";
|
|
19350
19573
|
function normalizeSource(value) {
|
|
19351
19574
|
if (!value) return "upload";
|
|
@@ -19404,7 +19627,7 @@ function registerUpCommand(program2) {
|
|
|
19404
19627
|
"Idempotency key for safe retries (Phase 2; logged only in v1)"
|
|
19405
19628
|
).action(async (cwdArg, options) => {
|
|
19406
19629
|
try {
|
|
19407
|
-
const cwd = cwdArg ?
|
|
19630
|
+
const cwd = cwdArg ? resolve3(cwdArg) : process.cwd();
|
|
19408
19631
|
if (cwdArg) process.chdir(cwd);
|
|
19409
19632
|
const source = normalizeSource(options.source);
|
|
19410
19633
|
const idempotencyKey = options.idempotencyKey?.trim();
|
|
@@ -19921,6 +20144,7 @@ registerAuthCommands(program);
|
|
|
19921
20144
|
registerAppsCommands(program);
|
|
19922
20145
|
registerDeployCommands(program);
|
|
19923
20146
|
registerInitCommand(program);
|
|
20147
|
+
registerAgentCommands(program);
|
|
19924
20148
|
registerUpCommand(program);
|
|
19925
20149
|
registerLogsCommand(program);
|
|
19926
20150
|
registerEnvCommands(program);
|