jioogen 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +225 -70
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -363,9 +363,9 @@ var require_utils = __commonJS({
|
|
|
363
363
|
fpath.split(self.sep).forEach(function(name) {
|
|
364
364
|
if (!name || name.substr(-1, 1) === ":") return;
|
|
365
365
|
resolvedPath += self.sep + name;
|
|
366
|
-
var
|
|
366
|
+
var stat2;
|
|
367
367
|
try {
|
|
368
|
-
|
|
368
|
+
stat2 = self.fs.statSync(resolvedPath);
|
|
369
369
|
} catch (e) {
|
|
370
370
|
if (e.message && e.message.startsWith("ENOENT")) {
|
|
371
371
|
self.fs.mkdirSync(resolvedPath);
|
|
@@ -373,7 +373,7 @@ var require_utils = __commonJS({
|
|
|
373
373
|
throw e;
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
|
-
if (
|
|
376
|
+
if (stat2 && stat2.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`);
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
mkdirSync(folder);
|
|
@@ -382,8 +382,8 @@ var require_utils = __commonJS({
|
|
|
382
382
|
const self = this;
|
|
383
383
|
if (self.fs.existsSync(path)) {
|
|
384
384
|
if (!overwrite) return false;
|
|
385
|
-
var
|
|
386
|
-
if (
|
|
385
|
+
var stat2 = self.fs.statSync(path);
|
|
386
|
+
if (stat2.isDirectory()) {
|
|
387
387
|
return false;
|
|
388
388
|
}
|
|
389
389
|
}
|
|
@@ -416,8 +416,8 @@ var require_utils = __commonJS({
|
|
|
416
416
|
const self = this;
|
|
417
417
|
self.fs.exists(path, function(exist) {
|
|
418
418
|
if (exist && !overwrite) return callback(false);
|
|
419
|
-
self.fs.stat(path, function(err,
|
|
420
|
-
if (exist &&
|
|
419
|
+
self.fs.stat(path, function(err, stat2) {
|
|
420
|
+
if (exist && stat2 && stat2.isDirectory()) {
|
|
421
421
|
return callback(false);
|
|
422
422
|
}
|
|
423
423
|
var folder = pth.dirname(path);
|
|
@@ -469,13 +469,13 @@ var require_utils = __commonJS({
|
|
|
469
469
|
for (const part of rel.split(pth.sep)) {
|
|
470
470
|
if (!part || part === ".") continue;
|
|
471
471
|
cur = pth.join(cur, part);
|
|
472
|
-
let
|
|
472
|
+
let stat2;
|
|
473
473
|
try {
|
|
474
|
-
|
|
474
|
+
stat2 = self.fs.lstatSync(cur);
|
|
475
475
|
} catch (e) {
|
|
476
476
|
break;
|
|
477
477
|
}
|
|
478
|
-
if (
|
|
478
|
+
if (stat2.isSymbolicLink()) throw Errors.FILE_IN_THE_WAY(`"${cur}"`);
|
|
479
479
|
}
|
|
480
480
|
};
|
|
481
481
|
Utils.prototype.findFiles = function(path) {
|
|
@@ -502,11 +502,11 @@ var require_utils = __commonJS({
|
|
|
502
502
|
self.fs.readdirSync(dir).forEach(function(file) {
|
|
503
503
|
const path2 = pth.join(dir, file);
|
|
504
504
|
if (escapesRoot(path2)) return;
|
|
505
|
-
const
|
|
505
|
+
const stat2 = self.fs.statSync(path2);
|
|
506
506
|
if (!pattern || pattern.test(path2)) {
|
|
507
|
-
files.push(pth.normalize(path2) + (
|
|
507
|
+
files.push(pth.normalize(path2) + (stat2.isDirectory() ? self.sep : ""));
|
|
508
508
|
}
|
|
509
|
-
if (
|
|
509
|
+
if (stat2.isDirectory() && recursive) {
|
|
510
510
|
const realDir = self.fs.realpathSync(path2);
|
|
511
511
|
if (!visited.has(realDir)) {
|
|
512
512
|
visited.add(realDir);
|
|
@@ -553,14 +553,14 @@ var require_utils = __commonJS({
|
|
|
553
553
|
if (!--pending) done();
|
|
554
554
|
return;
|
|
555
555
|
}
|
|
556
|
-
self.fs.stat(file, function(err3,
|
|
556
|
+
self.fs.stat(file, function(err3, stat2) {
|
|
557
557
|
if (err3) return done(err3);
|
|
558
|
-
if (!
|
|
558
|
+
if (!stat2) {
|
|
559
559
|
if (!--pending) done();
|
|
560
560
|
return;
|
|
561
561
|
}
|
|
562
|
-
results.push(pth.normalize(file) + (
|
|
563
|
-
if (!
|
|
562
|
+
results.push(pth.normalize(file) + (stat2.isDirectory() ? self.sep : ""));
|
|
563
|
+
if (!stat2.isDirectory()) {
|
|
564
564
|
if (!--pending) done();
|
|
565
565
|
return;
|
|
566
566
|
}
|
|
@@ -8433,8 +8433,11 @@ function envValue(name) {
|
|
|
8433
8433
|
const value = process.env[name]?.trim();
|
|
8434
8434
|
return value !== void 0 && value.length > 0 ? value : void 0;
|
|
8435
8435
|
}
|
|
8436
|
+
function configDirOverride() {
|
|
8437
|
+
return envValue(CONFIG_DIR_ENV) ?? envValue(LEGACY_CONFIG_DIR_ENV);
|
|
8438
|
+
}
|
|
8436
8439
|
function configDir() {
|
|
8437
|
-
const override =
|
|
8440
|
+
const override = configDirOverride();
|
|
8438
8441
|
return override !== void 0 ? resolve(override) : join2(homedir(), ".config", "jioogen");
|
|
8439
8442
|
}
|
|
8440
8443
|
function legacyConfigDir() {
|
|
@@ -8474,6 +8477,7 @@ async function readCredentialsFile(path) {
|
|
|
8474
8477
|
async function readCredentials() {
|
|
8475
8478
|
const primary = await readCredentialsFile(credentialsPath());
|
|
8476
8479
|
if (primary !== null) return primary;
|
|
8480
|
+
if (configDirOverride() !== void 0) return null;
|
|
8477
8481
|
const legacyPath = join2(legacyConfigDir(), CREDENTIALS_FILE);
|
|
8478
8482
|
if (legacyPath === credentialsPath()) return null;
|
|
8479
8483
|
return readCredentialsFile(legacyPath);
|
|
@@ -8558,6 +8562,10 @@ function isMissingFile(error) {
|
|
|
8558
8562
|
return isRecord(error) && error.code === "ENOENT";
|
|
8559
8563
|
}
|
|
8560
8564
|
|
|
8565
|
+
// src/errors.ts
|
|
8566
|
+
var UsageError = class extends Error {
|
|
8567
|
+
};
|
|
8568
|
+
|
|
8561
8569
|
// src/output.ts
|
|
8562
8570
|
function formatTable(headers, rows) {
|
|
8563
8571
|
const widths = headers.map(
|
|
@@ -8569,11 +8577,52 @@ function formatTable(headers, rows) {
|
|
|
8569
8577
|
}
|
|
8570
8578
|
|
|
8571
8579
|
// src/sidecar.ts
|
|
8572
|
-
import {
|
|
8580
|
+
import { randomUUID } from "node:crypto";
|
|
8581
|
+
import { mkdir as mkdir2, open, readFile as readFile3, rename, rm as rm2, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
8573
8582
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
8583
|
+
var LOCK_RETRY_MS = 25;
|
|
8584
|
+
var LOCK_TIMEOUT_MS = 3e3;
|
|
8585
|
+
var LOCK_STALE_MS = 1e4;
|
|
8574
8586
|
function sidecarPath(bundleDir) {
|
|
8575
8587
|
return join3(bundleDir, EXECUTION_SIDECAR_PATH);
|
|
8576
8588
|
}
|
|
8589
|
+
function lockPath(bundleDir) {
|
|
8590
|
+
return `${sidecarPath(bundleDir)}.lock`;
|
|
8591
|
+
}
|
|
8592
|
+
async function withStateLock(bundleDir, fn) {
|
|
8593
|
+
const target = lockPath(bundleDir);
|
|
8594
|
+
await mkdir2(dirname2(target), { recursive: true });
|
|
8595
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
8596
|
+
let handle = null;
|
|
8597
|
+
let staleRemoved = false;
|
|
8598
|
+
while (handle === null) {
|
|
8599
|
+
try {
|
|
8600
|
+
handle = await open(target, "wx");
|
|
8601
|
+
} catch (error) {
|
|
8602
|
+
if (!isLockBusy(error)) throw error;
|
|
8603
|
+
if (!staleRemoved && await removeStaleLock(target)) {
|
|
8604
|
+
staleRemoved = true;
|
|
8605
|
+
continue;
|
|
8606
|
+
}
|
|
8607
|
+
if (Date.now() >= deadline) {
|
|
8608
|
+
throw new Error(
|
|
8609
|
+
`Gagal mengambil lock sidecar setelah ${LOCK_TIMEOUT_MS / 1e3} detik: ${target}. Kemungkinan proses lain masih menulis; hapus file lock itu bila proses tersebut sudah mati.`
|
|
8610
|
+
);
|
|
8611
|
+
}
|
|
8612
|
+
await delay(LOCK_RETRY_MS);
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
try {
|
|
8616
|
+
await handle.writeFile(`${process.pid}
|
|
8617
|
+
`, "utf8");
|
|
8618
|
+
return await fn();
|
|
8619
|
+
} finally {
|
|
8620
|
+
await handle.close().catch(() => {
|
|
8621
|
+
});
|
|
8622
|
+
await rm2(target, { force: true }).catch(() => {
|
|
8623
|
+
});
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8577
8626
|
async function readState(bundleDir) {
|
|
8578
8627
|
let raw;
|
|
8579
8628
|
try {
|
|
@@ -8662,8 +8711,16 @@ async function writeState(bundleDir, state) {
|
|
|
8662
8711
|
const normalized = rebuild(state, state.tasks, state.current);
|
|
8663
8712
|
const target = sidecarPath(bundleDir);
|
|
8664
8713
|
await mkdir2(dirname2(target), { recursive: true });
|
|
8665
|
-
|
|
8714
|
+
const temp = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
8715
|
+
try {
|
|
8716
|
+
await writeFile2(temp, `${JSON.stringify(normalized, null, 2)}
|
|
8666
8717
|
`, "utf8");
|
|
8718
|
+
await rename(temp, target);
|
|
8719
|
+
} catch (error) {
|
|
8720
|
+
await rm2(temp, { force: true }).catch(() => {
|
|
8721
|
+
});
|
|
8722
|
+
throw error;
|
|
8723
|
+
}
|
|
8667
8724
|
return normalized;
|
|
8668
8725
|
}
|
|
8669
8726
|
function rebuild(state, tasks, current, now = /* @__PURE__ */ new Date()) {
|
|
@@ -8690,6 +8747,23 @@ function toTaskState(task) {
|
|
|
8690
8747
|
function isMissingFile2(error) {
|
|
8691
8748
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
8692
8749
|
}
|
|
8750
|
+
function isLockBusy(error) {
|
|
8751
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
8752
|
+
}
|
|
8753
|
+
async function removeStaleLock(target) {
|
|
8754
|
+
try {
|
|
8755
|
+
const info = await stat(target);
|
|
8756
|
+
if (Date.now() - info.mtimeMs < LOCK_STALE_MS) return false;
|
|
8757
|
+
await rm2(target, { force: true });
|
|
8758
|
+
return true;
|
|
8759
|
+
} catch (error) {
|
|
8760
|
+
if (isMissingFile2(error)) return true;
|
|
8761
|
+
throw error;
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
function delay(ms) {
|
|
8765
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8766
|
+
}
|
|
8693
8767
|
|
|
8694
8768
|
// src/skills.ts
|
|
8695
8769
|
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
@@ -9106,6 +9180,10 @@ async function extractBundle(bytes, targetDir) {
|
|
|
9106
9180
|
}
|
|
9107
9181
|
|
|
9108
9182
|
// src/commands.ts
|
|
9183
|
+
var PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9184
|
+
function isValidProjectId(projectId) {
|
|
9185
|
+
return PROJECT_ID_RE.test(projectId.trim());
|
|
9186
|
+
}
|
|
9109
9187
|
async function loginCommand(options) {
|
|
9110
9188
|
const token = options.token ?? envFirst("JIOOGEN_TOKEN", "JIOOCODE_TOKEN");
|
|
9111
9189
|
if (token !== void 0 && token.length > 0) {
|
|
@@ -9186,9 +9264,15 @@ async function projectsCommand(options) {
|
|
|
9186
9264
|
);
|
|
9187
9265
|
}
|
|
9188
9266
|
async function pullCommand(projectId, options) {
|
|
9267
|
+
if (!isValidProjectId(projectId)) {
|
|
9268
|
+
throw new UsageError(
|
|
9269
|
+
`Project id tidak valid: ${projectId}. Gunakan UUID dari \`jioogen projects\`.`
|
|
9270
|
+
);
|
|
9271
|
+
}
|
|
9272
|
+
await assertPullDefaultDir(process.cwd(), options.dir);
|
|
9189
9273
|
const { apiUrl, client } = await resolveApi(options);
|
|
9190
9274
|
const target = resolve4(options.dir ?? projectId);
|
|
9191
|
-
await assertPullTarget(target, projectId);
|
|
9275
|
+
await assertPullTarget(target, projectId, options.force === true);
|
|
9192
9276
|
const bytes = await client.getBytes(projectPath(projectId, "/bundle"));
|
|
9193
9277
|
const written = await extractBundle(bytes, target);
|
|
9194
9278
|
const skills = await installAgentSkills(target);
|
|
@@ -9264,9 +9348,15 @@ async function handoffCommand(options) {
|
|
|
9264
9348
|
}
|
|
9265
9349
|
}
|
|
9266
9350
|
async function taskNextCommand(options) {
|
|
9267
|
-
const {
|
|
9268
|
-
|
|
9269
|
-
|
|
9351
|
+
const { projectId, definitions, written, next } = await withLocalState(options, async (local) => {
|
|
9352
|
+
const written2 = await writeState(local.bundleDir, local.state);
|
|
9353
|
+
return {
|
|
9354
|
+
projectId: local.projectId,
|
|
9355
|
+
definitions: local.definitions,
|
|
9356
|
+
written: written2,
|
|
9357
|
+
next: selectNext(local.definitions, written2.tasks)
|
|
9358
|
+
};
|
|
9359
|
+
});
|
|
9270
9360
|
if (next === null) {
|
|
9271
9361
|
if (options.json === true) {
|
|
9272
9362
|
console.log(JSON.stringify({ done: true, projectId }, null, 2));
|
|
@@ -9315,62 +9405,71 @@ Status: ${next.state.status} \u2014 silakan ulangi.`);
|
|
|
9315
9405
|
Progress: ${written.counts?.done ?? 0}/${definitions.length} task selesai.`);
|
|
9316
9406
|
}
|
|
9317
9407
|
async function taskStartCommand(ref, options) {
|
|
9318
|
-
const
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9408
|
+
const task = await withLocalState(options, async (local) => {
|
|
9409
|
+
assertStartDependencies(ref, local.definitions, local.state.tasks);
|
|
9410
|
+
const updated = withStatus(local.state, ref, "in_progress", {
|
|
9411
|
+
awaitingCheckpoint: false,
|
|
9412
|
+
note: ""
|
|
9413
|
+
});
|
|
9414
|
+
await writeState(local.bundleDir, updated);
|
|
9415
|
+
return updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
|
|
9322
9416
|
});
|
|
9323
|
-
await writeState(bundleDir, updated);
|
|
9324
|
-
const task = updated.tasks.find((entry) => entry.ref.toUpperCase() === ref.toUpperCase());
|
|
9325
9417
|
console.log(`Started ${task?.ref ?? ref} (attempt ${task?.attempt ?? 1}).`);
|
|
9326
9418
|
}
|
|
9327
9419
|
async function taskCompleteCommand(ref, options) {
|
|
9328
|
-
const {
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
const completedTask = completed.tasks.find(
|
|
9334
|
-
(entry) => entry.ref.toUpperCase() === ref.toUpperCase()
|
|
9335
|
-
);
|
|
9336
|
-
const next = selectNext(definitions, completed.tasks);
|
|
9337
|
-
if (completedTask !== void 0 && next !== null && isBoundary(completedTask, next.task)) {
|
|
9338
|
-
const withCheckpoint = setCurrent(completed, {
|
|
9339
|
-
ref: next.task.id,
|
|
9340
|
-
...next.task.layer !== void 0 ? { layer: next.task.layer } : {},
|
|
9341
|
-
phase: next.task.phase,
|
|
9342
|
-
awaitingCheckpoint: true
|
|
9420
|
+
const { completedTask, next, checkpoint } = await withLocalState(options, async (local) => {
|
|
9421
|
+
assertCompleteDependencies(ref, local.definitions, local.state.tasks, options.force === true);
|
|
9422
|
+
const completed = withStatus(local.state, ref, "done", {
|
|
9423
|
+
note: options.note ?? "",
|
|
9424
|
+
awaitingCheckpoint: false
|
|
9343
9425
|
});
|
|
9344
|
-
|
|
9345
|
-
|
|
9426
|
+
const completedTask2 = completed.tasks.find(
|
|
9427
|
+
(entry) => entry.ref.toUpperCase() === ref.toUpperCase()
|
|
9428
|
+
);
|
|
9429
|
+
const next2 = selectNext(local.definitions, completed.tasks);
|
|
9430
|
+
if (completedTask2 !== void 0 && next2 !== null && isBoundary(completedTask2, next2.task)) {
|
|
9431
|
+
const withCheckpoint = setCurrent(completed, {
|
|
9432
|
+
ref: next2.task.id,
|
|
9433
|
+
...next2.task.layer !== void 0 ? { layer: next2.task.layer } : {},
|
|
9434
|
+
phase: next2.task.phase,
|
|
9435
|
+
awaitingCheckpoint: true
|
|
9436
|
+
});
|
|
9437
|
+
await writeState(local.bundleDir, withCheckpoint);
|
|
9438
|
+
return { completedTask: completedTask2, next: next2, checkpoint: true };
|
|
9439
|
+
}
|
|
9440
|
+
await writeState(local.bundleDir, completed);
|
|
9441
|
+
return { completedTask: completedTask2, next: next2, checkpoint: false };
|
|
9442
|
+
});
|
|
9443
|
+
console.log(`Done ${completedTask?.ref ?? ref}.`);
|
|
9444
|
+
if (checkpoint && next !== null) {
|
|
9346
9445
|
console.log(
|
|
9347
9446
|
`Checkpoint berikutnya: ${next.task.id} (${next.task.layer ?? "-"} / phase ${next.task.phase}).`
|
|
9348
9447
|
);
|
|
9349
9448
|
console.log("Berhenti dan minta konfirmasi user sebelum task start berikutnya.");
|
|
9350
9449
|
return;
|
|
9351
9450
|
}
|
|
9352
|
-
await writeState(bundleDir, completed);
|
|
9353
|
-
console.log(`Done ${completedTask?.ref ?? ref}.`);
|
|
9354
9451
|
if (next !== null) console.log(`Berikutnya: ${next.task.id} \u2014 ${next.task.title}`);
|
|
9355
9452
|
}
|
|
9356
9453
|
async function taskFailCommand(ref, reasonParts, options) {
|
|
9357
9454
|
const reason = reasonParts.join(" ").trim() || options.note || "";
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9455
|
+
await withLocalState(options, async (local) => {
|
|
9456
|
+
const updated = withStatus(local.state, ref, "failed", {
|
|
9457
|
+
note: reason,
|
|
9458
|
+
awaitingCheckpoint: false
|
|
9459
|
+
});
|
|
9460
|
+
await writeState(local.bundleDir, updated);
|
|
9362
9461
|
});
|
|
9363
|
-
await writeState(bundleDir, updated);
|
|
9364
9462
|
console.log(`Failed ${ref}${reason.length > 0 ? `: ${reason}` : "."}`);
|
|
9365
9463
|
}
|
|
9366
9464
|
async function taskResetCommand(ref, reasonParts, options) {
|
|
9367
9465
|
const reason = reasonParts.join(" ").trim() || options.note || "";
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9466
|
+
await withLocalState(options, async (local) => {
|
|
9467
|
+
const updated = withStatus(local.state, ref, "todo", {
|
|
9468
|
+
note: reason,
|
|
9469
|
+
awaitingCheckpoint: false
|
|
9470
|
+
});
|
|
9471
|
+
await writeState(local.bundleDir, updated);
|
|
9372
9472
|
});
|
|
9373
|
-
await writeState(bundleDir, updated);
|
|
9374
9473
|
console.log(`Reset ${ref} ke todo${reason.length > 0 ? `: ${reason}` : "."}`);
|
|
9375
9474
|
}
|
|
9376
9475
|
async function statusCommand(options) {
|
|
@@ -9381,6 +9480,23 @@ async function statusCommand(options) {
|
|
|
9381
9480
|
const { bundleDir, projectId } = await resolveLocalBundle(options);
|
|
9382
9481
|
const existing = await readState(bundleDir);
|
|
9383
9482
|
if (existing === null) {
|
|
9483
|
+
if (options.json === true) {
|
|
9484
|
+
console.log(
|
|
9485
|
+
JSON.stringify(
|
|
9486
|
+
{
|
|
9487
|
+
projectId,
|
|
9488
|
+
hasExecution: false,
|
|
9489
|
+
tasks: [],
|
|
9490
|
+
counts: summarizeExecutionCounts([]),
|
|
9491
|
+
current: null,
|
|
9492
|
+
updatedAt: null
|
|
9493
|
+
},
|
|
9494
|
+
null,
|
|
9495
|
+
2
|
|
9496
|
+
)
|
|
9497
|
+
);
|
|
9498
|
+
return;
|
|
9499
|
+
}
|
|
9384
9500
|
console.log(`Project ${projectId}: belum ada sidecar. Jalankan \`jioogen task next\`.`);
|
|
9385
9501
|
return;
|
|
9386
9502
|
}
|
|
@@ -9487,14 +9603,48 @@ async function resolveBundle(options) {
|
|
|
9487
9603
|
const api = await resolveApi({ api: options.api ?? local.linkedApiUrl });
|
|
9488
9604
|
return { ...api, ...local };
|
|
9489
9605
|
}
|
|
9490
|
-
async function
|
|
9606
|
+
async function withLocalState(options, fn) {
|
|
9491
9607
|
const context = await resolveLocalBundle(options);
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9608
|
+
return withStateLock(context.bundleDir, async () => {
|
|
9609
|
+
const definitions = await loadTasks(context.bundleDir);
|
|
9610
|
+
const existing = await readState(context.bundleDir);
|
|
9611
|
+
const state = existing === null ? createState(context.projectId, definitions) : mergeTasks(existing, definitions);
|
|
9612
|
+
return fn({ ...context, definitions, state });
|
|
9613
|
+
});
|
|
9614
|
+
}
|
|
9615
|
+
function unmetDependencies(ref, definitions, states) {
|
|
9616
|
+
const key = ref.toUpperCase();
|
|
9617
|
+
const definition = definitions.find((task) => task.id.toUpperCase() === key);
|
|
9618
|
+
if (definition === void 0) return [];
|
|
9619
|
+
const byRef = new Map(states.map((state) => [state.ref.toUpperCase(), state]));
|
|
9620
|
+
return definition.dependsOn.filter(
|
|
9621
|
+
(dependency) => byRef.get(dependency.toUpperCase())?.status !== "done"
|
|
9622
|
+
);
|
|
9623
|
+
}
|
|
9624
|
+
function assertStartDependencies(ref, definitions, states) {
|
|
9625
|
+
const missing = unmetDependencies(ref, definitions, states);
|
|
9626
|
+
if (missing.length === 0) return;
|
|
9627
|
+
throw new Error(
|
|
9628
|
+
`Tidak bisa memulai ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu.`
|
|
9629
|
+
);
|
|
9630
|
+
}
|
|
9631
|
+
function assertCompleteDependencies(ref, definitions, states, force) {
|
|
9632
|
+
if (force) return;
|
|
9633
|
+
const missing = unmetDependencies(ref, definitions, states);
|
|
9634
|
+
if (missing.length === 0) return;
|
|
9635
|
+
throw new Error(
|
|
9636
|
+
`Tidak bisa menyelesaikan ${ref}: dependency belum selesai (${missing.join(", ")}). Selesaikan dependency itu lebih dulu, atau tambahkan --force bila memang disengaja.`
|
|
9637
|
+
);
|
|
9496
9638
|
}
|
|
9497
|
-
async function
|
|
9639
|
+
async function assertPullDefaultDir(cwd, dirOption) {
|
|
9640
|
+
if (dirOption) return;
|
|
9641
|
+
if (await hasBundleMarkers(cwd)) {
|
|
9642
|
+
throw new Error(
|
|
9643
|
+
`Direktori ${cwd} adalah bundle JiooGen. Gunakan \`jioogen link <projectId>\` untuk mengikat folder ini, atau \`--dir <dir>\` eksplisit untuk mengunduh ke folder lain.`
|
|
9644
|
+
);
|
|
9645
|
+
}
|
|
9646
|
+
}
|
|
9647
|
+
async function assertPullTarget(target, projectId, force = false) {
|
|
9498
9648
|
let entries;
|
|
9499
9649
|
try {
|
|
9500
9650
|
entries = await readdir3(target);
|
|
@@ -9510,6 +9660,11 @@ async function assertPullTarget(target, projectId) {
|
|
|
9510
9660
|
if (link.projectId !== projectId) {
|
|
9511
9661
|
throw new Error(`Direktori ${target} terikat ke project ${link.projectId}.`);
|
|
9512
9662
|
}
|
|
9663
|
+
if (!force) {
|
|
9664
|
+
throw new Error(
|
|
9665
|
+
`Direktori ${target} sudah berisi bundle project ini; pull ulang akan menimpa edit lokal. Jalankan \`jioogen link ${projectId}\` bila hanya ingin mengikat, atau ulangi dengan --force.`
|
|
9666
|
+
);
|
|
9667
|
+
}
|
|
9513
9668
|
}
|
|
9514
9669
|
async function assertLinkTarget(target, projectId) {
|
|
9515
9670
|
if (!await hasBundleMarkers(target)) {
|
|
@@ -9605,7 +9760,7 @@ function isMissingFile4(error) {
|
|
|
9605
9760
|
}
|
|
9606
9761
|
|
|
9607
9762
|
// src/index.ts
|
|
9608
|
-
var VERSION = "0.2.
|
|
9763
|
+
var VERSION = "0.2.3";
|
|
9609
9764
|
var USAGE = `jioogen \u2014 CLI JiooGen (task selection lokal, server read-only)
|
|
9610
9765
|
|
|
9611
9766
|
Usage:
|
|
@@ -9614,14 +9769,14 @@ Usage:
|
|
|
9614
9769
|
jioogen logout
|
|
9615
9770
|
jioogen whoami
|
|
9616
9771
|
jioogen projects
|
|
9617
|
-
jioogen pull <projectId> [--dir <dir>]
|
|
9772
|
+
jioogen pull <projectId> [--dir <dir>] [--force]
|
|
9618
9773
|
jioogen link <projectId> [--dir <dir>]
|
|
9619
9774
|
jioogen skills install [--dir <dir>]
|
|
9620
9775
|
jioogen check [--dir <dir>] [--json]
|
|
9621
9776
|
jioogen handoff [--project <id>] [--json]
|
|
9622
9777
|
jioogen task next [--json]
|
|
9623
9778
|
jioogen task start <ref>
|
|
9624
|
-
jioogen task complete <ref> [--note <text>]
|
|
9779
|
+
jioogen task complete <ref> [--note <text>] [--force]
|
|
9625
9780
|
jioogen task fail <ref> [reason...] [--note <text>]
|
|
9626
9781
|
jioogen task reset <ref> [reason...] [--note <text>]
|
|
9627
9782
|
jioogen status [--remote] [--json]
|
|
@@ -9637,8 +9792,6 @@ Global options:
|
|
|
9637
9792
|
-h, --help Tampilkan bantuan
|
|
9638
9793
|
-v, --version Tampilkan versi
|
|
9639
9794
|
`;
|
|
9640
|
-
var UsageError = class extends Error {
|
|
9641
|
-
};
|
|
9642
9795
|
async function main() {
|
|
9643
9796
|
let raw = {};
|
|
9644
9797
|
let positionals = [];
|
|
@@ -9655,6 +9808,7 @@ async function main() {
|
|
|
9655
9808
|
project: { type: "string" },
|
|
9656
9809
|
dir: { type: "string" },
|
|
9657
9810
|
remote: { type: "boolean" },
|
|
9811
|
+
force: { type: "boolean" },
|
|
9658
9812
|
files: { type: "string" },
|
|
9659
9813
|
note: { type: "string" },
|
|
9660
9814
|
json: { type: "boolean" },
|
|
@@ -9680,6 +9834,7 @@ async function main() {
|
|
|
9680
9834
|
project: text(raw.project),
|
|
9681
9835
|
dir: text(raw.dir),
|
|
9682
9836
|
remote: flag(raw.remote),
|
|
9837
|
+
force: flag(raw.force),
|
|
9683
9838
|
files: text(raw.files),
|
|
9684
9839
|
note: text(raw.note),
|
|
9685
9840
|
json: flag(raw.json)
|