@hitch42/applet 0.1.1-beta.20 → 0.1.1-beta.22
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/_internal/client.d.ts +0 -2
- package/dist/_internal/client.js +3 -5
- package/dist/_internal/log-context.d.ts +0 -2
- package/dist/_internal/log-context.js +1 -2
- package/dist/_internal/server.d.ts +2 -10
- package/dist/_internal/server.js +9 -14
- package/dist/applet.js +64 -23
- package/dist/auth-DuMRqSGe.js +49 -0
- package/dist/{batch-_ZjAg-Kk.js → batch-DwyWmiaE.js} +3 -4
- package/dist/build-C7hi2yg4.js +1 -0
- package/dist/{build-D47vK5aa.js → build-FAQSHJgm.js} +4 -3
- package/dist/{ci-BTFTCBgy.js → ci-oWlUJKm9.js} +5 -6
- package/dist/client/hitch/client.d.ts +1 -1
- package/dist/client/hitch/client.js +1 -2
- package/dist/client/hitch/phone.d.ts +1 -1
- package/dist/client/hitch/phone.js +1 -2
- package/dist/client/hitch.d.ts +2 -2
- package/dist/client/hitch.js +2 -2
- package/dist/client/react/markdown-editor.d.ts +1 -1
- package/dist/client/react/markdown-editor.js +1 -2
- package/dist/client/react/markdown.d.ts +1 -1
- package/dist/client/react/markdown.js +1 -2
- package/dist/client/react.d.ts +2 -2
- package/dist/client/react.js +2 -2
- package/dist/client.d.ts +2 -4
- package/dist/client.js +3 -5
- package/dist/{contract-CJv-n1l3.d.ts → contract-CUc0B2rl.d.ts} +1 -3
- package/dist/{contract-BeKz1Qcs.js → contract-D9LCNGjD.js} +1 -2
- package/dist/{create-C2UrZYSK.d.ts → create-BJqLT8Uq.d.ts} +0 -4
- package/dist/{deploy-BbqyK5Ik.js → deploy-BM55ZoSp.js} +160 -152
- package/dist/{device-BgIfytaR.js → device-CZWWckCq.js} +54 -34
- package/dist/{email-protocol-B9u_dh2p.d.ts → email-protocol-BWja_v7c.d.ts} +16 -16
- package/dist/generate-DG9vVkBO.js +1 -0
- package/dist/{generate-BLD0DoCk.js → generate-DNgO44RO.js} +32 -37
- package/dist/generated-agents-BUnJ0FQA.js +215 -0
- package/dist/git-credential-CU7-Erzs.js +24 -0
- package/dist/git-deploy-C12nMNB6.js +290 -0
- package/dist/git-deploy-CNz6mxiF.js +1 -0
- package/dist/git-migration-CIq-sN61.js +629 -0
- package/dist/git-safety-Ck3X7jvA.js +13 -0
- package/dist/{headers-CeRX-Xji.js → headers-BcEMIVCo.js} +1 -2
- package/dist/{hitch-client-Cw-WZ_8v.js → hitch-client-CNN-dU6d.js} +5 -7
- package/dist/{hitch-client-DtyTZzP5.d.ts → hitch-client-k7_jIjPw.d.ts} +5 -7
- package/dist/{hitch-file-url-BPKj9zVG.js → hitch-file-url--AMjDOzP.js} +1 -2
- package/dist/index.d.ts +13 -17
- package/dist/index.js +33 -38
- package/dist/{init-BKUBf6i9.js → init-BD5mC_9L.js} +70 -18
- package/dist/{lazy-client-CwIzYAoE.js → lazy-client--QCL0wiu.js} +1 -2
- package/dist/{load-config-DGALUExv.js → load-config-Cffvj-aM.js} +24 -26
- package/dist/{log-context-DWznGjfz.js → log-context-BepwdjrR.js} +1 -2
- package/dist/login-BWZKo7Ui.js +21 -0
- package/dist/{logs-Cv5z1e2w.js → logs-glCVHI-8.js} +6 -6
- package/dist/{manifest-BBHD_EY9.d.ts → manifest-CpghlaJc.d.ts} +6 -7
- package/dist/manifest-patterns-BfxqImlp.js +4 -0
- package/dist/{markdown-CwlqzBtP.js → markdown-BA_XzFxq.js} +2 -3
- package/dist/{markdown-DSHOQIOb.d.ts → markdown-CnB35mjc.d.ts} +1 -3
- package/dist/{markdown-editor-C5mLHGqu.js → markdown-editor-CaUuphfc.js} +3 -5
- package/dist/{markdown-editor-BmEaBzd7.d.ts → markdown-editor-xF3uATBp.d.ts} +1 -5
- package/dist/parent-D52Kxp8U.js +1 -0
- package/dist/parent-Zx8Eg1Ki.js +75 -0
- package/dist/{project-CrxdZR0B.js → project-DZGk765E.js} +63 -54
- package/dist/{prompt-ChS-grYO.js → prompt-CW98R-Un.js} +1 -2
- package/dist/{query-cache-CXmAyLv3.js → query-cache-DQJy45RT.js} +1 -2
- package/dist/{query-utils-zz9pVjgp.d.ts → query-utils-okxIoKM3.d.ts} +0 -2
- package/dist/{schedules-Dajgni_z.js → schedules-CSUb84e2.js} +5 -5
- package/dist/server.d.ts +5 -13
- package/dist/server.js +17 -20
- package/dist/source-hash-BbvtRaus.js +107 -0
- package/dist/store-E5NhITHl.js +448 -0
- package/dist/{upload-DRbVhuDm.js → upload-D1mzpsSk.js} +4 -6
- package/dist/{upload-D_kkmne_.d.ts → upload-wp-Ilsv6.d.ts} +6 -10
- package/dist/{use-phone-ug4B5gQp.js → use-phone-CB_idmrz.js} +9 -13
- package/dist/{use-phone-M7c_HsB7.d.ts → use-phone-DBAkasPq.d.ts} +0 -4
- package/dist/{variables-B-CLu8cY.js → variables-CgZstC7I.js} +7 -8
- package/dist/vite.d.ts +1 -3
- package/dist/vite.js +41 -50
- package/dist/{workflow-DurM6Fwx.d.ts → workflow-BnBrJIzu.d.ts} +0 -2
- package/dist/{workflow-engine-BOTzURKM.js → workflow-engine-CyyaVENo.js} +2 -4
- package/dist/{workflow-CTo2kZF1.js → workflow-l2SlVePB.js} +2 -4
- package/dist/workspace-CtIKSgMD.js +76 -0
- package/package.json +6 -5
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +27 -9
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +4 -4
- package/scaffold/.agents/skills/hitch-applets/references/files.md +2 -2
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -6
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +2 -2
- package/scaffold/.agents/skills/hitch-applets/references/records.md +8 -8
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +52 -43
- package/scaffold/AGENTS.md +6 -0
- package/dist/auth-D3CrCtZq.js +0 -21
- package/dist/build-JtbY8q2p.js +0 -2
- package/dist/generate-COCRy1Bs.js +0 -2
- package/dist/login-CceGvlMi.js +0 -14
- package/dist/store-B8N_8JIN.js +0 -196
- package/dist/workspace-8yqXsvce.js +0 -31
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AnyContractRouter } from "@orpc/contract";
|
|
2
2
|
import { ClientPromiseResult } from "@orpc/client";
|
|
3
|
-
//#region src/hitch/batch.d.ts
|
|
4
3
|
type BatchVerb = "create" | "update" | "delete" | "updateWhere";
|
|
5
4
|
type BatchTxRecords<Records> = { [Code in keyof Records]: { [Verb in keyof Records[Code] & BatchVerb]: Records[Code][Verb] extends ((...args: infer Args) => unknown) ? (input: Verb extends "create" ? Args[0] extends {
|
|
6
5
|
body: infer Body;
|
|
@@ -31,8 +30,6 @@ type WithBatchRecorder<T> = T extends {
|
|
|
31
30
|
*/
|
|
32
31
|
batch: BatchRecorderCall<T, Batch> & Batch;
|
|
33
32
|
} : T;
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/hitch/create.d.ts
|
|
36
33
|
type Many<T> = Extract<T, readonly unknown[]>;
|
|
37
34
|
type One<T> = Exclude<T, readonly unknown[]>;
|
|
38
35
|
type SplitCreate<F> = F extends ((input: infer Input extends {
|
|
@@ -68,5 +65,4 @@ type WithRecordCreateCalls<Utils, Client> = Utils extends {
|
|
|
68
65
|
call: SplitCreate<Clients[Code][Verb]>;
|
|
69
66
|
} : Records[Code][Verb] : Records[Code][Verb] : Records[Code][Verb]; }; };
|
|
70
67
|
} : Utils : Utils;
|
|
71
|
-
//#endregion
|
|
72
68
|
export { WithBatchRecorder as i, WithRecordCreates as n, WithSingleRecordCreates as r, WithRecordCreateCalls as t };
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { n as readWorkspaceConfig } from "./workspace-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as findWorkspaceRoot, n as findAppletRoot, o as isRootLayout, r as findParentRoot, s as readWorkspaceConfig } from "./workspace-CtIKSgMD.js";
|
|
2
|
+
import { n as fetchWithOrgAuth, r as orgUsesHitchGit, t as canCheckHitchGit } from "./auth-DuMRqSGe.js";
|
|
3
|
+
import { n as loadAppletSourceDefinition } from "./load-config-Cffvj-aM.js";
|
|
4
|
+
import { i as prepareAppletProject } from "./project-DZGk765E.js";
|
|
5
|
+
import { a as loadGitHubActionsSources, n as githubActionsContext, t as fetchWithGitHubActions } from "./ci-oWlUJKm9.js";
|
|
6
|
+
import { n as buildPreparedApplet, r as prepareWorkspaceBuild, t as buildApplet } from "./build-FAQSHJgm.js";
|
|
7
|
+
import { n as gitEnvironment, r as sourceHashes, t as SourceHashUnavailableError } from "./source-hash-BbvtRaus.js";
|
|
8
|
+
import { a as prepareGitDeploy, o as pushStagedCommit, s as refreshHitchMain, t as deployErrorMessage } from "./git-deploy-C12nMNB6.js";
|
|
9
|
+
import { n as childApplets, r as childHasChanges } from "./parent-Zx8Eg1Ki.js";
|
|
10
|
+
import { n as migrateWorkspace } from "./git-migration-CIq-sN61.js";
|
|
5
11
|
import { createRequire } from "node:module";
|
|
6
|
-
import { createHash } from "node:crypto";
|
|
7
12
|
import { existsSync } from "node:fs";
|
|
8
|
-
import {
|
|
9
|
-
import { dirname, relative, resolve, sep } from "node:path";
|
|
10
|
-
import { execFile
|
|
13
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
14
|
+
import { basename, dirname, relative, resolve, sep } from "node:path";
|
|
15
|
+
import { execFile } from "node:child_process";
|
|
11
16
|
import { promisify } from "node:util";
|
|
12
17
|
import { createGzip } from "node:zlib";
|
|
13
|
-
|
|
18
|
+
// \0rolldown/runtime.js
|
|
14
19
|
var __create = Object.create;
|
|
15
20
|
var __defProp = Object.defineProperty;
|
|
16
21
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
17
22
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
23
|
var __getProtoOf = Object.getPrototypeOf;
|
|
19
24
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
20
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
25
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
|
|
21
26
|
var __copyProps = (to, from, except, desc) => {
|
|
22
27
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
23
28
|
key = keys[i];
|
|
@@ -33,13 +38,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
38
|
enumerable: true
|
|
34
39
|
}) : target, mod));
|
|
35
40
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
36
|
-
|
|
37
|
-
//#region ../../node_modules/.pnpm/events-universal@1.0.1/node_modules/events-universal/default.js
|
|
41
|
+
// ../../node_modules/.pnpm/events-universal@1.0.1/node_modules/events-universal/default.js
|
|
38
42
|
var require_default = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
39
43
|
module.exports = __require("events");
|
|
40
44
|
}));
|
|
41
|
-
|
|
42
|
-
//#region ../../node_modules/.pnpm/fast-fifo@1.3.2/node_modules/fast-fifo/fixed-size.js
|
|
45
|
+
// ../../node_modules/.pnpm/fast-fifo@1.3.2/node_modules/fast-fifo/fixed-size.js
|
|
43
46
|
var require_fixed_size = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
44
47
|
module.exports = class FixedFIFO {
|
|
45
48
|
constructor(hwm) {
|
|
@@ -76,8 +79,7 @@ var require_fixed_size = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
76
79
|
}
|
|
77
80
|
};
|
|
78
81
|
}));
|
|
79
|
-
|
|
80
|
-
//#region ../../node_modules/.pnpm/fast-fifo@1.3.2/node_modules/fast-fifo/index.js
|
|
82
|
+
// ../../node_modules/.pnpm/fast-fifo@1.3.2/node_modules/fast-fifo/index.js
|
|
81
83
|
var require_fast_fifo = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
82
84
|
const FixedFIFO = require_fixed_size();
|
|
83
85
|
module.exports = class FastFIFO {
|
|
@@ -121,8 +123,7 @@ var require_fast_fifo = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
121
123
|
}
|
|
122
124
|
};
|
|
123
125
|
}));
|
|
124
|
-
|
|
125
|
-
//#region ../../node_modules/.pnpm/b4a@1.8.1/node_modules/b4a/index.js
|
|
126
|
+
// ../../node_modules/.pnpm/b4a@1.8.1/node_modules/b4a/index.js
|
|
126
127
|
var require_b4a = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
127
128
|
function isBuffer(value) {
|
|
128
129
|
return Buffer.isBuffer(value) || value instanceof Uint8Array;
|
|
@@ -276,8 +277,7 @@ var require_b4a = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
276
277
|
writeUInt32LE
|
|
277
278
|
};
|
|
278
279
|
}));
|
|
279
|
-
|
|
280
|
-
//#region ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/lib/pass-through-decoder.js
|
|
280
|
+
// ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/lib/pass-through-decoder.js
|
|
281
281
|
var require_pass_through_decoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
282
282
|
const b4a = require_b4a();
|
|
283
283
|
module.exports = class PassThroughDecoder {
|
|
@@ -295,8 +295,7 @@ var require_pass_through_decoder = /* @__PURE__ */ __commonJSMin(((exports, modu
|
|
|
295
295
|
}
|
|
296
296
|
};
|
|
297
297
|
}));
|
|
298
|
-
|
|
299
|
-
//#region ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/lib/utf8-decoder.js
|
|
298
|
+
// ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/lib/utf8-decoder.js
|
|
300
299
|
var require_utf8_decoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
301
300
|
const b4a = require_b4a();
|
|
302
301
|
/**
|
|
@@ -443,8 +442,7 @@ var require_utf8_decoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
443
442
|
return 0;
|
|
444
443
|
}
|
|
445
444
|
}));
|
|
446
|
-
|
|
447
|
-
//#region ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/index.js
|
|
445
|
+
// ../../node_modules/.pnpm/text-decoder@1.2.7/node_modules/text-decoder/index.js
|
|
448
446
|
var require_text_decoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
449
447
|
const PassThroughDecoder = require_pass_through_decoder();
|
|
450
448
|
const UTF8Decoder = require_utf8_decoder();
|
|
@@ -495,8 +493,7 @@ var require_text_decoder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
495
493
|
}
|
|
496
494
|
}
|
|
497
495
|
}));
|
|
498
|
-
|
|
499
|
-
//#region ../../node_modules/.pnpm/streamx@2.28.1/node_modules/streamx/lib/errors.js
|
|
496
|
+
// ../../node_modules/.pnpm/streamx@2.28.1/node_modules/streamx/lib/errors.js
|
|
500
497
|
var require_errors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
501
498
|
module.exports = class StreamError extends Error {
|
|
502
499
|
constructor(msg, code, fn = StreamError) {
|
|
@@ -533,8 +530,7 @@ var require_errors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
533
530
|
}
|
|
534
531
|
};
|
|
535
532
|
}));
|
|
536
|
-
|
|
537
|
-
//#region ../../node_modules/.pnpm/streamx@2.28.1/node_modules/streamx/index.js
|
|
533
|
+
// ../../node_modules/.pnpm/streamx@2.28.1/node_modules/streamx/index.js
|
|
538
534
|
var require_streamx = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
539
535
|
const { EventEmitter } = require_default();
|
|
540
536
|
const FIFO = require_fast_fifo();
|
|
@@ -1485,8 +1481,7 @@ var require_streamx = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
1485
1481
|
PassThrough
|
|
1486
1482
|
};
|
|
1487
1483
|
}));
|
|
1488
|
-
|
|
1489
|
-
//#region ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/headers.js
|
|
1484
|
+
// ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/headers.js
|
|
1490
1485
|
var require_headers = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1491
1486
|
const b4a = require_b4a();
|
|
1492
1487
|
const ZEROS = "0000000000000000000";
|
|
@@ -1721,8 +1716,7 @@ var require_headers = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
1721
1716
|
return len + digits + str;
|
|
1722
1717
|
}
|
|
1723
1718
|
}));
|
|
1724
|
-
|
|
1725
|
-
//#region ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/extract.js
|
|
1719
|
+
// ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/extract.js
|
|
1726
1720
|
var require_extract = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1727
1721
|
const { Writable, Readable, getStreamError } = require_streamx();
|
|
1728
1722
|
const FIFO = require_fast_fifo();
|
|
@@ -2070,8 +2064,7 @@ var require_extract = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2070
2064
|
return size && 512 - size;
|
|
2071
2065
|
}
|
|
2072
2066
|
}));
|
|
2073
|
-
|
|
2074
|
-
//#region ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/constants.js
|
|
2067
|
+
// ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/constants.js
|
|
2075
2068
|
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2076
2069
|
const constants = {
|
|
2077
2070
|
S_IFMT: 61440,
|
|
@@ -2087,8 +2080,7 @@ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2087
2080
|
module.exports = constants;
|
|
2088
2081
|
}
|
|
2089
2082
|
}));
|
|
2090
|
-
|
|
2091
|
-
//#region ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/pack.js
|
|
2083
|
+
// ../../node_modules/.pnpm/tar-stream@3.2.1/node_modules/tar-stream/pack.js
|
|
2092
2084
|
var require_pack = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
2093
2085
|
const { Readable, Writable, getStreamError } = require_streamx();
|
|
2094
2086
|
const b4a = require_b4a();
|
|
@@ -2308,113 +2300,17 @@ var require_pack = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
2308
2300
|
return b4a.isBuffer(buf) ? buf : b4a.from(buf);
|
|
2309
2301
|
}
|
|
2310
2302
|
}));
|
|
2311
|
-
|
|
2312
|
-
//#region src/source-hash.ts
|
|
2303
|
+
// src/response-error.ts
|
|
2313
2304
|
var import_tar_stream = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
2314
2305
|
exports.extract = require_extract();
|
|
2315
2306
|
exports.pack = require_pack();
|
|
2316
2307
|
})))(), 1);
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2308
|
+
async function hitchResponseError(response, summary) {
|
|
2309
|
+
const body = await response.json().catch(() => null);
|
|
2310
|
+
const details = body !== null && typeof body === "object" ? ["code" in body && typeof body.code === "string" ? body.code : void 0, "message" in body && typeof body.message === "string" ? body.message : void 0].filter((value) => value !== void 0).join(": ") : "";
|
|
2311
|
+
return /* @__PURE__ */ new Error(`${summary} (${response.status})${details ? `: ${details}` : ""}.`);
|
|
2320
2312
|
}
|
|
2321
|
-
|
|
2322
|
-
constructor() {
|
|
2323
|
-
super("Git is unavailable or the applet is outside a git repository.");
|
|
2324
|
-
}
|
|
2325
|
-
};
|
|
2326
|
-
function isMissingPath(error) {
|
|
2327
|
-
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
2328
|
-
}
|
|
2329
|
-
async function hashFiles(topLevel, paths, env) {
|
|
2330
|
-
if (paths.length === 0) return [];
|
|
2331
|
-
return new Promise((resolveHashes, reject) => {
|
|
2332
|
-
const child = spawn("git", ["hash-object", "--stdin-paths"], {
|
|
2333
|
-
cwd: topLevel,
|
|
2334
|
-
env
|
|
2335
|
-
});
|
|
2336
|
-
const stdout = [];
|
|
2337
|
-
const stderr = [];
|
|
2338
|
-
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
2339
|
-
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
2340
|
-
child.stdin.on("error", reject);
|
|
2341
|
-
child.on("error", reject);
|
|
2342
|
-
child.on("close", (code) => {
|
|
2343
|
-
if (code !== 0) {
|
|
2344
|
-
reject(/* @__PURE__ */ new Error(`git hash-object failed (${code}): ${Buffer.concat(stderr).toString("utf8").trim()}`));
|
|
2345
|
-
return;
|
|
2346
|
-
}
|
|
2347
|
-
const hashes = Buffer.concat(stdout).toString("utf8").trimEnd().split("\n");
|
|
2348
|
-
if (hashes.length !== paths.length || hashes.some((hash) => !/^[0-9a-f]{40,64}$/.test(hash))) {
|
|
2349
|
-
reject(/* @__PURE__ */ new Error("git hash-object returned an unexpected number of hashes."));
|
|
2350
|
-
return;
|
|
2351
|
-
}
|
|
2352
|
-
resolveHashes(hashes);
|
|
2353
|
-
});
|
|
2354
|
-
child.stdin.end(paths.map((path) => `"${path.split("").map((character) => character < " " || character === "" || character === "\"" || character === "\\" ? `\\${character.charCodeAt(0).toString(8).padStart(3, "0")}` : character).join("")}"\n`).join(""));
|
|
2355
|
-
});
|
|
2356
|
-
}
|
|
2357
|
-
async function sourceHashes(appletRoots) {
|
|
2358
|
-
if (appletRoots.length === 0) return /* @__PURE__ */ new Map();
|
|
2359
|
-
const env = gitEnvironment();
|
|
2360
|
-
const workspaceRoot = dirname(dirname(appletRoots[0]));
|
|
2361
|
-
let topLevel;
|
|
2362
|
-
try {
|
|
2363
|
-
const { stdout } = await execFileAsync$1("git", ["rev-parse", "--show-toplevel"], {
|
|
2364
|
-
cwd: workspaceRoot,
|
|
2365
|
-
env
|
|
2366
|
-
});
|
|
2367
|
-
topLevel = stdout.trim();
|
|
2368
|
-
} catch (error) {
|
|
2369
|
-
if (isMissingPath(error) || error instanceof Error && error.message.includes("not a git repository")) throw new SourceHashUnavailableError();
|
|
2370
|
-
throw error;
|
|
2371
|
-
}
|
|
2372
|
-
const { stdout } = await execFileAsync$1("git", [
|
|
2373
|
-
"ls-files",
|
|
2374
|
-
"-z",
|
|
2375
|
-
"--cached",
|
|
2376
|
-
"--others",
|
|
2377
|
-
"--exclude-standard"
|
|
2378
|
-
], {
|
|
2379
|
-
cwd: topLevel,
|
|
2380
|
-
env,
|
|
2381
|
-
maxBuffer: 67108864
|
|
2382
|
-
});
|
|
2383
|
-
const paths = [...new Set(stdout.split("\0").filter(Boolean))];
|
|
2384
|
-
const present = (await Promise.all(paths.map(async (path) => {
|
|
2385
|
-
const absolutePath = resolve(topLevel, path);
|
|
2386
|
-
try {
|
|
2387
|
-
const stat = await lstat(absolutePath);
|
|
2388
|
-
if (stat.isFile()) return {
|
|
2389
|
-
path,
|
|
2390
|
-
kind: "file"
|
|
2391
|
-
};
|
|
2392
|
-
if (stat.isSymbolicLink()) return {
|
|
2393
|
-
path,
|
|
2394
|
-
kind: "link",
|
|
2395
|
-
contentId: await readlink(absolutePath)
|
|
2396
|
-
};
|
|
2397
|
-
return;
|
|
2398
|
-
} catch (error) {
|
|
2399
|
-
if (isMissingPath(error)) return void 0;
|
|
2400
|
-
throw error;
|
|
2401
|
-
}
|
|
2402
|
-
}))).filter((entry) => entry !== void 0);
|
|
2403
|
-
const regularPaths = present.filter((entry) => entry.kind === "file").map((entry) => entry.path);
|
|
2404
|
-
const hashes = await hashFiles(topLevel, regularPaths, env);
|
|
2405
|
-
const contentIds = /* @__PURE__ */ new Map();
|
|
2406
|
-
present.filter((entry) => entry.kind === "link").forEach((entry) => contentIds.set(entry.path, entry.contentId));
|
|
2407
|
-
regularPaths.forEach((path, index) => contentIds.set(path, hashes[index]));
|
|
2408
|
-
const appletsDirectory = relative(topLevel, resolve(workspaceRoot, "applets")).split(sep).join("/");
|
|
2409
|
-
return new Map(appletRoots.map((appletRoot) => {
|
|
2410
|
-
const appletDirectory = relative(topLevel, appletRoot).split(sep).join("/");
|
|
2411
|
-
const digest = createHash("sha256");
|
|
2412
|
-
[...contentIds.entries()].filter(([path]) => path !== appletsDirectory && !path.startsWith(`${appletsDirectory}/`) || path.startsWith(`${appletDirectory}/`)).sort(([left], [right]) => Buffer.compare(Buffer.from(left), Buffer.from(right))).forEach(([path, contentId]) => digest.update(`${path}\0${contentId}\n`));
|
|
2413
|
-
return [appletRoot, digest.digest("hex")];
|
|
2414
|
-
}));
|
|
2415
|
-
}
|
|
2416
|
-
//#endregion
|
|
2417
|
-
//#region src/deploy.ts
|
|
2313
|
+
// src/deploy.ts
|
|
2418
2314
|
const execFileAsync = promisify(execFile);
|
|
2419
2315
|
var WorkspaceDeployError = class extends Error {
|
|
2420
2316
|
constructor(failedApplets) {
|
|
@@ -2499,15 +2395,18 @@ function releaseForm(manifest, bundle) {
|
|
|
2499
2395
|
body.set("bundle", new File([bundle], "bundle.tar.gz", { type: "application/gzip" }));
|
|
2500
2396
|
return body;
|
|
2501
2397
|
}
|
|
2502
|
-
async function deployWithOrgAuth(appletRoot, distDir, manifest, sourceHash) {
|
|
2398
|
+
async function deployWithOrgAuth(appletRoot, distDir, manifest, sourceHash, gitSha) {
|
|
2503
2399
|
const fetcher = globalThis.fetch;
|
|
2504
|
-
const [bundle, git] = await Promise.all([createBundle(distDir), readGitInfo(appletRoot)
|
|
2400
|
+
const [bundle, git] = await Promise.all([createBundle(distDir), gitSha === void 0 ? readGitInfo(appletRoot) : Promise.resolve({
|
|
2401
|
+
gitDirty: false,
|
|
2402
|
+
gitSha
|
|
2403
|
+
})]);
|
|
2505
2404
|
return fetchWithOrgAuth(manifest.org, (instanceUrl, accessToken) => {
|
|
2506
2405
|
const body = releaseForm(manifest, bundle);
|
|
2507
2406
|
body.set("gitDirty", String(git.gitDirty));
|
|
2508
2407
|
if (git.gitSha !== void 0) body.set("gitSha", git.gitSha);
|
|
2509
2408
|
if (sourceHash !== void 0) body.set("sourceHash", sourceHash);
|
|
2510
|
-
return fetcher(`${instanceUrl}/api/v1/applets/${manifest.code}/releases`, {
|
|
2409
|
+
return fetcher(`${instanceUrl}/api/applet/v1/applets/${manifest.code}/releases`, {
|
|
2511
2410
|
method: "POST",
|
|
2512
2411
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
2513
2412
|
body
|
|
@@ -2521,23 +2420,35 @@ async function deployWithGitHubActions(context, appletRoot, distDir, manifest, s
|
|
|
2521
2420
|
const body = releaseForm(manifest, bundle);
|
|
2522
2421
|
body.set("source_directory", sourceDirectory);
|
|
2523
2422
|
if (sourceHash !== void 0) body.set("source_hash", sourceHash);
|
|
2524
|
-
return fetcher(`${instanceUrl}/api/
|
|
2423
|
+
return fetcher(`${instanceUrl}/api/applet/v1/ci/applets/${manifest.code}/releases`, {
|
|
2525
2424
|
method: "POST",
|
|
2526
2425
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
2527
2426
|
body
|
|
2528
2427
|
});
|
|
2529
2428
|
});
|
|
2530
2429
|
}
|
|
2531
|
-
async function
|
|
2532
|
-
const github = githubActionsContext();
|
|
2533
|
-
sourceHash ??= (await computeSourceHash([appletRoot], github !== void 0)).get(appletRoot);
|
|
2430
|
+
async function buildRelease(appletRoot, options) {
|
|
2534
2431
|
if (options.dist === void 0) await buildApplet(appletRoot);
|
|
2535
2432
|
const distDir = resolve(appletRoot, options.dist ?? ".hitch/dist");
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2433
|
+
return {
|
|
2434
|
+
distDir,
|
|
2435
|
+
manifest: await readManifest(distDir)
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
async function uploadRelease(appletRoot, distDir, manifest, stagedSha, logPrefix = "", sourceHash) {
|
|
2439
|
+
const github = githubActionsContext();
|
|
2440
|
+
sourceHash ??= (await computeSourceHash([appletRoot], github !== void 0)).get(appletRoot);
|
|
2441
|
+
const response = github === void 0 ? await deployWithOrgAuth(appletRoot, distDir, manifest, sourceHash, stagedSha) : await deployWithGitHubActions(github, appletRoot, distDir, manifest, sourceHash);
|
|
2442
|
+
if (!response.ok) {
|
|
2443
|
+
const body = await response.clone().json().catch(() => null);
|
|
2444
|
+
const code = body !== null && typeof body === "object" && "code" in body && typeof body.code === "string" ? body.code : void 0;
|
|
2445
|
+
const message = deployErrorMessage(code);
|
|
2446
|
+
if (message !== void 0) throw new Error(message);
|
|
2447
|
+
throw await hitchResponseError(response, "Deploy failed");
|
|
2448
|
+
}
|
|
2539
2449
|
const result = await response.json();
|
|
2540
2450
|
console.log(`${logPrefix}Deployed release ${result.data.id} (${result.promoted ? "promoted" : "not promoted"}).`);
|
|
2451
|
+
if (stagedSha !== void 0 && result.promoted) console.log(`${logPrefix}main is now ${stagedSha.slice(0, 7)}.`);
|
|
2541
2452
|
if (result.changeset_id !== void 0) console.log(`${logPrefix}The release goes live when changeset ${result.changeset_id} applies.`);
|
|
2542
2453
|
if (result.warnings?.length) {
|
|
2543
2454
|
for (const warning of result.warnings) console.warn(`${logPrefix}Warning: ${warning.message}`);
|
|
@@ -2546,6 +2457,15 @@ async function deployApplet(appletRoot, options, logPrefix = "", sourceHash) {
|
|
|
2546
2457
|
if (result.unset_variables?.length) console.warn(`${logPrefix}Warning: declared variables not set: ${result.unset_variables.join(", ")}. Set them with applet variables set.`);
|
|
2547
2458
|
return result;
|
|
2548
2459
|
}
|
|
2460
|
+
async function deployApplet(appletRoot, options, logPrefix = "", sourceHash, stagedSha) {
|
|
2461
|
+
const github = githubActionsContext();
|
|
2462
|
+
sourceHash ??= (await computeSourceHash([appletRoot], github !== void 0)).get(appletRoot);
|
|
2463
|
+
const { distDir, manifest } = await buildRelease(appletRoot, options);
|
|
2464
|
+
if (stagedSha !== void 0 && manifest.code !== basename(appletRoot)) throw new Error(`The built applet code ${manifest.code} must match its folder ${basename(appletRoot)}.`);
|
|
2465
|
+
if (stagedSha !== void 0 && manifest.org !== readWorkspaceConfig(findWorkspaceRoot(appletRoot) ?? dirname(dirname(appletRoot)))?.org) throw new Error("The built applet organization must match hitch.config.ts.");
|
|
2466
|
+
if (stagedSha !== void 0) await pushStagedCommit(findWorkspaceRoot(appletRoot) ?? dirname(dirname(appletRoot)), manifest.org, stagedSha);
|
|
2467
|
+
return uploadRelease(appletRoot, distDir, manifest, stagedSha, logPrefix, sourceHash);
|
|
2468
|
+
}
|
|
2549
2469
|
function buildConcurrency() {
|
|
2550
2470
|
const value = process.env.APPLET_BUILD_CONCURRENCY;
|
|
2551
2471
|
if (value === void 0) return Infinity;
|
|
@@ -2568,15 +2488,20 @@ function semaphore(limit) {
|
|
|
2568
2488
|
}
|
|
2569
2489
|
};
|
|
2570
2490
|
}
|
|
2571
|
-
async function workspaceAppletRoots(workspaceRoot) {
|
|
2491
|
+
async function workspaceAppletRoots(workspaceRoot, allowEmpty = false) {
|
|
2572
2492
|
const appletsDir = resolve(workspaceRoot, "applets");
|
|
2573
2493
|
const roots = (await readdir(appletsDir, { withFileTypes: true }).catch((error) => {
|
|
2574
2494
|
if (isErrno(error, "ENOENT")) return [];
|
|
2575
2495
|
throw error;
|
|
2576
2496
|
})).filter((entry) => entry.isDirectory()).map((entry) => resolve(appletsDir, entry.name)).filter((root) => existsSync(resolve(root, "applet.manifest.ts"))).sort();
|
|
2577
|
-
if (roots.length === 0) throw new Error(`No applets were found under ${appletsDir}.`);
|
|
2497
|
+
if (!allowEmpty && roots.length === 0) throw new Error(`No applets were found under ${appletsDir}.`);
|
|
2578
2498
|
return roots;
|
|
2579
2499
|
}
|
|
2500
|
+
function validateSingleApplet(roots) {
|
|
2501
|
+
if (roots.length === 1) return;
|
|
2502
|
+
const names = roots.map((root) => basename(root)).join(", ");
|
|
2503
|
+
throw new Error(`Hitch git keeps one applet per repo. This workspace has ${roots.length} applets: ${names}. Move each applet into its own workspace.`);
|
|
2504
|
+
}
|
|
2580
2505
|
async function computeSourceHash(appletRoots, required) {
|
|
2581
2506
|
try {
|
|
2582
2507
|
return await sourceHashes(appletRoots);
|
|
@@ -2604,9 +2529,70 @@ async function ciDeployDecisions(github, appletRoots, hashes) {
|
|
|
2604
2529
|
}];
|
|
2605
2530
|
}));
|
|
2606
2531
|
}
|
|
2532
|
+
async function deployRootApplet(root, options, flagChecked = false) {
|
|
2533
|
+
const config = readWorkspaceConfig(root);
|
|
2534
|
+
if (!flagChecked && !await orgUsesHitchGit(config.org)) throw new Error(`Hitch git is not switched on for ${config.org}.`);
|
|
2535
|
+
await prepareAppletProject(root);
|
|
2536
|
+
const definition = await loadAppletSourceDefinition(root);
|
|
2537
|
+
const sha = await prepareGitDeploy(root, definition.code, config.org, options.message, options.dist !== void 0);
|
|
2538
|
+
const { distDir, manifest } = await buildRelease(root, options);
|
|
2539
|
+
if (manifest.code !== definition.code || manifest.org !== config.org) throw new Error("The built applet identity must match applet.manifest.ts.");
|
|
2540
|
+
await pushStagedCommit(root, manifest.org, sha);
|
|
2541
|
+
await uploadRelease(root, distDir, manifest, sha);
|
|
2542
|
+
}
|
|
2607
2543
|
async function runDeploy(options) {
|
|
2608
2544
|
const root = process.cwd();
|
|
2609
2545
|
const github = githubActionsContext();
|
|
2546
|
+
if (process.env.GITHUB_ACTIONS !== "true") {
|
|
2547
|
+
const workspaceRoot = findWorkspaceRoot(root);
|
|
2548
|
+
const parent = workspaceRoot === void 0 ? findParentRoot(root) : void 0;
|
|
2549
|
+
if (parent !== void 0) {
|
|
2550
|
+
const failed = [];
|
|
2551
|
+
for (const child of await childApplets(parent)) try {
|
|
2552
|
+
const config = readWorkspaceConfig(child);
|
|
2553
|
+
if (!await orgUsesHitchGit(config.org)) throw new Error(`Hitch git is not switched on for ${config.org}.`);
|
|
2554
|
+
await refreshHitchMain(child, config.org, (await loadAppletSourceDefinition(child)).code);
|
|
2555
|
+
if (!await childHasChanges(child)) {
|
|
2556
|
+
console.log(`[${basename(child)}] Skipped: no changes.`);
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
await deployRootApplet(child, options, true);
|
|
2560
|
+
console.log(`[${basename(child)}] Deployed.`);
|
|
2561
|
+
} catch (error) {
|
|
2562
|
+
failed.push(basename(child));
|
|
2563
|
+
console.error(`[${basename(child)}] ${error instanceof Error ? error.message : String(error)}`);
|
|
2564
|
+
}
|
|
2565
|
+
if (failed.length > 0) throw new WorkspaceDeployError(failed);
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
if (workspaceRoot === void 0) throw new Error("Run applet deploy in an applet, workspace or parent folder.");
|
|
2569
|
+
const config = readWorkspaceConfig(workspaceRoot);
|
|
2570
|
+
const enabled = isRootLayout(workspaceRoot) ? await orgUsesHitchGit(config.org) : await canCheckHitchGit() && await orgUsesHitchGit(config.org);
|
|
2571
|
+
if (isRootLayout(workspaceRoot)) {
|
|
2572
|
+
if (!enabled) throw new Error(`Hitch git is not switched on for ${config.org}.`);
|
|
2573
|
+
await deployRootApplet(workspaceRoot, options, true);
|
|
2574
|
+
return;
|
|
2575
|
+
}
|
|
2576
|
+
if (enabled) {
|
|
2577
|
+
const selected = findAppletRoot(root);
|
|
2578
|
+
const migrated = await migrateWorkspace(workspaceRoot, options.message);
|
|
2579
|
+
const roots = selected === void 0 ? migrated : migrated.filter((path) => migrated.length === 1 || basename(path) === basename(selected));
|
|
2580
|
+
const failed = [];
|
|
2581
|
+
for (const appletRoot of roots) try {
|
|
2582
|
+
await deployRootApplet(appletRoot, options, true);
|
|
2583
|
+
} catch (error) {
|
|
2584
|
+
failed.push(appletRoot);
|
|
2585
|
+
console.error(`[${basename(appletRoot)}] ${error instanceof Error ? error.message : String(error)}`);
|
|
2586
|
+
}
|
|
2587
|
+
if (failed.length > 0) throw new WorkspaceDeployError(failed);
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if (root !== workspaceRoot && !existsSync(resolve(root, "applet.manifest.ts"))) throw new Error("Run applet deploy in an applet folder or in the workspace root.");
|
|
2591
|
+
if (root !== workspaceRoot) {
|
|
2592
|
+
await deployApplet(findAppletRoot(root), options);
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2610
2596
|
if (existsSync(resolve(root, "applet.manifest.ts"))) {
|
|
2611
2597
|
await deployApplet(root, options);
|
|
2612
2598
|
return;
|
|
@@ -2650,5 +2636,27 @@ async function runDeploy(options) {
|
|
|
2650
2636
|
});
|
|
2651
2637
|
if (failedApplets.length > 0) throw new WorkspaceDeployError(failedApplets);
|
|
2652
2638
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2639
|
+
async function runRollback(releaseId) {
|
|
2640
|
+
const cwd = process.cwd();
|
|
2641
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
2642
|
+
const config = workspaceRoot === void 0 ? void 0 : readWorkspaceConfig(workspaceRoot);
|
|
2643
|
+
if (config === void 0 || workspaceRoot === void 0) throw new Error("Run applet rollback in an applet folder or in the workspace root.");
|
|
2644
|
+
const roots = isRootLayout(workspaceRoot) ? [workspaceRoot] : await workspaceAppletRoots(workspaceRoot, true);
|
|
2645
|
+
validateSingleApplet(roots);
|
|
2646
|
+
const code = (await loadAppletSourceDefinition(roots[0])).code;
|
|
2647
|
+
const response = await fetchWithOrgAuth(config.org, (organizationUrl, accessToken) => globalThis.fetch(`${organizationUrl}/api/manage/v1/applets/${code}/releases/${encodeURIComponent(releaseId)}/promote`, {
|
|
2648
|
+
method: "POST",
|
|
2649
|
+
headers: {
|
|
2650
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2651
|
+
"Content-Type": "application/json"
|
|
2652
|
+
},
|
|
2653
|
+
body: JSON.stringify({ rollback: true })
|
|
2654
|
+
}));
|
|
2655
|
+
if (!response.ok) throw await hitchResponseError(response, "Rollback failed");
|
|
2656
|
+
const result = await response.json();
|
|
2657
|
+
const release = result !== null && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
2658
|
+
const sha = release !== null && typeof release === "object" && "git_sha" in release && typeof release.git_sha === "string" ? release.git_sha : void 0;
|
|
2659
|
+
if (sha === void 0) throw new Error("The rollback response has no commit id.");
|
|
2660
|
+
console.log(`Rolled back ${code} to release ${releaseId} (commit ${sha.slice(0, 7)}). main moved back. Your local branch still has the newer commits: revert or reset them before the next deploy.`);
|
|
2661
|
+
}
|
|
2662
|
+
export { runDeploy, runRollback };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { l as
|
|
1
|
+
import { c as normalizeInstanceUrl, d as rejectEnvironmentKey, g as saveOAuthCredentials, l as organizationInstanceUrl, o as credentialStorageMessage, t as AuthenticationError, u as readCredentialStore } from "./store-E5NhITHl.js";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import readline from "node:readline/promises";
|
|
4
|
+
// ../shared/auth/src/device.mts
|
|
5
|
+
const CLIENT_ID = "hitch-cli-oauth";
|
|
5
6
|
const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
6
7
|
function defaultOpenUrl(url) {
|
|
7
8
|
try {
|
|
@@ -40,13 +41,10 @@ function parseDeviceCode(value) {
|
|
|
40
41
|
return value;
|
|
41
42
|
}
|
|
42
43
|
function parseToken(value) {
|
|
43
|
-
return typeof value.access_token === "string" && typeof value.token_type === "string" ? value : void 0;
|
|
44
|
+
return typeof value.access_token === "string" && typeof value.refresh_token === "string" && typeof value.expires_in === "number" && typeof value.token_type === "string" ? value : void 0;
|
|
44
45
|
}
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
function parseMe(value) {
|
|
46
|
+
function parseMe(input) {
|
|
47
|
+
const value = requiredObject(input, "Hitch returned an invalid identity response.");
|
|
50
48
|
const person = requiredObject(value.person, "Hitch returned an invalid identity response.");
|
|
51
49
|
if (typeof person.id !== "string" || typeof person.firstName !== "string" || typeof person.lastName !== "string" || typeof person.email !== "string" || !Array.isArray(value.memberships)) throw new Error("Hitch returned an invalid identity response.");
|
|
52
50
|
const organization = value.organization === void 0 || value.organization === null ? null : requiredObject(value.organization, "Hitch returned an invalid identity response.");
|
|
@@ -62,24 +60,30 @@ function parseMe(value) {
|
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
62
|
async function fetchMe(fetcher, instanceUrl, auth) {
|
|
65
|
-
const response = await fetcher(`${instanceUrl}/api/v1
|
|
63
|
+
const response = await fetcher(`${instanceUrl}/api/me/v1`, { headers: { Authorization: `Bearer ${auth.accessToken}` } });
|
|
66
64
|
rejectEnvironmentKey(response, auth);
|
|
67
65
|
if (auth.credentialType === "apiKey" && response.status === 401) throw new AuthenticationError("API key rejected.");
|
|
68
66
|
const body = await jsonResponse(response);
|
|
69
67
|
if (!response.ok) throw new AuthenticationError(typeof body.message === "string" ? body.message : `Failed to load the signed-in identity (${response.status}).`);
|
|
70
68
|
return parseMe(body);
|
|
71
69
|
}
|
|
72
|
-
async function pollForToken(fetcher, instanceUrl, device, now, sleep) {
|
|
70
|
+
async function pollForToken(fetcher, instanceUrl, resource, device, now, sleep) {
|
|
73
71
|
const deadline = now() + device.expires_in * 1e3;
|
|
74
72
|
let interval = (device.interval ?? 5) * 1e3;
|
|
75
73
|
while (now() < deadline) {
|
|
76
74
|
await sleep(Math.min(interval, deadline - now()));
|
|
77
75
|
if (now() >= deadline) break;
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
const response = await fetcher(`${instanceUrl}/api/auth/oauth2/token`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
79
|
+
body: new URLSearchParams({
|
|
80
|
+
grant_type: DEVICE_GRANT,
|
|
81
|
+
device_code: device.device_code,
|
|
82
|
+
client_id: CLIENT_ID,
|
|
83
|
+
resource
|
|
84
|
+
})
|
|
82
85
|
});
|
|
86
|
+
const body = await jsonResponse(response);
|
|
83
87
|
const token = parseToken(body);
|
|
84
88
|
if (response.ok && token !== void 0) return token;
|
|
85
89
|
switch (body.error) {
|
|
@@ -97,39 +101,55 @@ async function pollForToken(fetcher, instanceUrl, device, now, sleep) {
|
|
|
97
101
|
async function loginWithDevice(instanceUrl, options = {}) {
|
|
98
102
|
const fetcher = globalThis.fetch;
|
|
99
103
|
const now = Date.now;
|
|
100
|
-
const
|
|
104
|
+
const storedOrgs = Object.keys((await readCredentialStore()).instances[normalizeInstanceUrl(instanceUrl)] ?? {});
|
|
105
|
+
let orgCode = options.expectedOrg ?? process.env.HITCH_DEFAULT_ORG ?? (storedOrgs.length === 1 ? storedOrgs[0] : void 0);
|
|
106
|
+
if (orgCode === void 0 && process.stdin.isTTY === true) {
|
|
107
|
+
const terminal = readline.createInterface({
|
|
108
|
+
input: process.stdin,
|
|
109
|
+
output: process.stdout
|
|
110
|
+
});
|
|
111
|
+
try {
|
|
112
|
+
orgCode = (await terminal.question("Organization code: ")).trim();
|
|
113
|
+
} finally {
|
|
114
|
+
terminal.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!orgCode) throw new AuthenticationError("Select an organization with --org or HITCH_DEFAULT_ORG.");
|
|
118
|
+
const organizationUrl = organizationInstanceUrl(instanceUrl, orgCode);
|
|
119
|
+
const resource = `${organizationUrl}/mcp`;
|
|
120
|
+
const { body, response } = await postJson(fetcher, `${instanceUrl}/api/auth/device/code`, {
|
|
121
|
+
client_id: CLIENT_ID,
|
|
122
|
+
scope: "openid offline_access",
|
|
123
|
+
resource
|
|
124
|
+
});
|
|
101
125
|
if (!response.ok) throw new AuthenticationError(typeof body.error_description === "string" ? body.error_description : `Could not start sign-in (${response.status}).`);
|
|
102
126
|
const device = parseDeviceCode(body);
|
|
103
|
-
const verificationUrl =
|
|
127
|
+
const verificationUrl = device.verification_uri_complete;
|
|
104
128
|
console.log(`Open ${verificationUrl} and confirm code ${device.user_code}.`);
|
|
105
129
|
if (!options.noBrowser) try {
|
|
106
130
|
defaultOpenUrl(verificationUrl);
|
|
107
131
|
} catch {}
|
|
108
|
-
const token = await pollForToken(fetcher, instanceUrl, device, now, (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
109
|
-
const
|
|
110
|
-
if (!organizationResponse.ok) throw new AuthenticationError("The sign-in did not resolve an organization.");
|
|
111
|
-
const organization = parseDeviceOrganization(organizationBody);
|
|
112
|
-
const me = await fetchMe(fetcher, instanceUrl, {
|
|
132
|
+
const token = await pollForToken(fetcher, instanceUrl, resource, device, now, (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
133
|
+
const me = await fetchMe(fetcher, organizationUrl, {
|
|
113
134
|
accessToken: token.access_token,
|
|
114
135
|
credentialType: "session",
|
|
115
136
|
source: "store"
|
|
116
137
|
});
|
|
117
|
-
const membership = me.memberships.find(({
|
|
118
|
-
if (membership === void 0) throw new AuthenticationError(
|
|
119
|
-
const credentials = {
|
|
120
|
-
type: "
|
|
138
|
+
const membership = me.memberships.find(({ code }) => code === orgCode);
|
|
139
|
+
if (membership === void 0) throw new AuthenticationError(`You are not a member of the Hitch organization "${orgCode}".`);
|
|
140
|
+
const credentials = await saveOAuthCredentials(instanceUrl, {
|
|
141
|
+
type: "oauth",
|
|
121
142
|
accessToken: token.access_token,
|
|
122
|
-
expiresAt:
|
|
123
|
-
organizationId:
|
|
124
|
-
orgCode
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
if (
|
|
143
|
+
expiresAt: now() + token.expires_in * 1e3,
|
|
144
|
+
organizationId: membership.orgId,
|
|
145
|
+
orgCode
|
|
146
|
+
}, token.refresh_token);
|
|
147
|
+
const storageMessage = credentialStorageMessage();
|
|
148
|
+
if (storageMessage !== void 0) console.log(storageMessage);
|
|
128
149
|
return {
|
|
129
150
|
credentials,
|
|
130
151
|
me,
|
|
131
152
|
membership
|
|
132
153
|
};
|
|
133
154
|
}
|
|
134
|
-
|
|
135
|
-
export { loginWithDevice as t };
|
|
155
|
+
export { parseMe as n, loginWithDevice as t };
|