prosicht 1.5.0 → 1.6.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/README.md CHANGED
@@ -38,7 +38,16 @@ Tür yüklemeyle birlikte gönderilir (`manifest.kind`); sunucu ayrıca dosyalar
38
38
 
39
39
  Yalnızca `index.html`, `css/`, `js/` ve `assets/` yüklenir (en fazla 50 MB, dosya başına 25 MB, 500 dosya). `/` ile başlayan yollar için uyarı verilir. Kayıtlı key panelde yenilendiyse CLI yenisini sorar.
40
40
 
41
- Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla; tür klasörden tespit edilir).
41
+ Seçenekler: `--key <key>`, `--host <url>`, `-y` (soruları atla; tür klasörden tespit edilir), `-f, --force` (aşağıdaki iki denetimi atla, üzerine yaz).
42
+
43
+ ### Ortak çalışma (CLI 1.6+)
44
+
45
+ İçeriğin sahibi Pro veya Ekip planındaysa panelden başka hesapları (ücretsiz olabilir) içeriğe ekler. Eklenen kişi panelde **Benimle paylaşılanlar** altından:
46
+ 1. **Kaynak dosyalarını indir** ile yayındaki sürümü ZIP olarak alır ve açar (içinde `AGENTS.md` ve key'siz `.prosicht/publish.json` var),
47
+ 2. **Yayın keyi al** ile kendine ait keyi alır,
48
+ 3. klasörde `npx prosicht publish` çalıştırır; key ilk seferde sorulur.
49
+
50
+ Her yayından sonra klasörün hangi sürümün üzerine kurulu olduğu `.prosicht/publish.json`'a (`baseVersion`) yazılır ve sonraki yayında gönderilir. Bu arada başka biri (sahip ya da başka bir ortak çalışan) yeni sürüm yayınladıysa yayın reddedilir; güncel kaynağı indirip değişiklikleri aktarın ya da bilerek üzerine yazmak için `--force` kullanın. Klasörün ait olduğu içerikten başka bir içeriğin keyi girilirse de yayın reddedilir (`--force` ile o içeriğe yayınlanır). Sahibin planı ortak çalışmayı artık içermiyorsa ortak çalışanların yayını reddedilir.
42
51
 
43
52
  ## Sunum hazırlama
44
53
 
package/index.js CHANGED
@@ -282,6 +282,7 @@ program
282
282
  .option('-k, --key <key>', 'Panelden alinan 32 karakterlik yayin keyi')
283
283
  .option('--host <url>', 'Yayin adresi (varsayilan: https://deck.prosicht.com)')
284
284
  .option('-y, --yes', 'Sorulari atla: tur klasorden tespit edilir, key kaydedilir')
285
+ .option('-f, --force', 'Bu klasorden sonra yayinlanan surumun ve klasorun ait oldugu icerigin denetimini atla (uzerine yazar)')
285
286
  .action((options) => publish(options));
286
287
 
287
288
  program.parse(process.argv);
package/lib/publish.js CHANGED
@@ -62,12 +62,17 @@ async function chooseHost(saved, flag) {
62
62
  return normalizeHost(custom);
63
63
  }
64
64
 
65
- async function chooseKey(saved, flag) {
65
+ async function chooseKey(saved, flag, config = {}) {
66
66
  if (flag) return KEY_PATTERN.test(flag) ? flag : fail('Key 32 karakter olmali (harf ve rakam).');
67
67
  if (saved && KEY_PATTERN.test(saved)) {
68
68
  console.log(`Kayitli key kullaniliyor (....${saved.slice(-4)}). Degistirmek icin: --key`);
69
69
  return saved;
70
70
  }
71
+ // panelden indirilen klasör: hangi içeriğe ait olduğu belli, key kişiye özel
72
+ if (config.deckId && config.url) {
73
+ console.log(`Bu klasor ${config.url} icerigine ait. Keyinizi panelden alin:`);
74
+ console.log(' sahibiyseniz Iceriklerim, ortak calisiyorsaniz Benimle paylasilanlar -> Yayin keyi al.');
75
+ }
71
76
  return ask({
72
77
  type: 'password', name: 'key', message: 'Lutfen keyi giriniz',
73
78
  validate: (v) => (KEY_PATTERN.test(String(v).trim()) ? true : 'Key 32 karakter olmali (harf ve rakam)'),
@@ -75,10 +80,13 @@ async function chooseKey(saved, flag) {
75
80
  });
76
81
  }
77
82
 
78
- // Sunucu türü dosyalardan da anlar; kind yine de gönderilir (panelde rozet, MCP'de doğru kit)
79
- async function upload(host, key, files, title, kind) {
83
+ // Sunucu türü dosyalardan da anlar; kind yine de gönderilir (panelde rozet, MCP'de doğru kit).
84
+ // base: klasörün geldiği içerik ve sürüm; arada başkası yayınladıysa sunucu reddeder (409)
85
+ async function upload(host, key, files, title, kind, base) {
80
86
  const form = new FormData();
81
- form.append('manifest', JSON.stringify({ title, kind, files: files.map((f) => ({ path: f.path, size: f.size })) }));
87
+ const manifest = { title, kind, files: files.map((f) => ({ path: f.path, size: f.size })) };
88
+ if (base) Object.assign(manifest, { deckId: base.deckId, baseVersion: base.version });
89
+ form.append('manifest', JSON.stringify(manifest));
82
90
  for (const f of files) {
83
91
  const type = MIME_TYPES[path.extname(f.path).toLowerCase()];
84
92
  form.append('files', new Blob([fs.readFileSync(f.abs)], { type }), path.basename(f.path));
@@ -100,6 +108,22 @@ const PANEL_CHOICE = { presentation: 'Sunum', blog: 'Blog yazisi' };
100
108
 
101
109
  function explain(status, body) {
102
110
  if (status === 401) fail('Key gecersiz veya yenilenmis. Panelden guncel keyi alin.');
111
+ if (status === 403 && body.code === 'collaboration_inactive') {
112
+ fail('Icerigin sahibinin plani ortak calismayi icermiyor (Pro veya Ekip gerekir); su an yalnizca sahibi yayinlayabilir.');
113
+ }
114
+ // indirildikten (ya da son yayından) sonra başka biri yeni sürüm yayınlamış
115
+ if (status === 409 && body.code === 'version_conflict') {
116
+ fail(`Bu klasor surum ${body.baseVersion} uzerine kurulu, ama bu arada surum ${body.version} yayinlanmis. Yayinlarsaniz o surumdeki degisiklikler kaybolur.`, [
117
+ 'Panelden "Kaynak dosyalarini indir" ile guncel surumu alip degisikliklerinizi ona aktarin, sonra tekrar yayinlayin.',
118
+ 'Ya da bilerek uzerine yazmak icin: npx prosicht publish --force'
119
+ ]);
120
+ }
121
+ if (status === 409 && body.code === 'deck_mismatch') {
122
+ fail(`Bu klasor ${body.folderDeckId} icerigine ait, girilen key ise ${body.deckId} icerigine.`, [
123
+ 'Klasorun ait oldugu icerigin keyini --key ile verin.',
124
+ `Klasoru bilerek ${body.deckId} uzerine yayinlamak icin: npx prosicht publish --force`
125
+ ]);
126
+ }
103
127
  // key, daha önce başka türde yayınlanmış bir içeriğe ait
104
128
  if (status === 409 && DATIVE[body.deckKind] && NOUN[body.kind]) {
105
129
  fail(`Bu key bir ${DATIVE[body.deckKind]} ait; bu klasordeki icerik bir ${NOUN[body.kind]}.`, [
@@ -145,14 +169,18 @@ async function publish(options = {}) {
145
169
 
146
170
  const config = readConfig(root);
147
171
  const host = await chooseHost(options.host ? null : config.host, options.host);
148
- let key = await chooseKey(options.key ? null : config.key, options.key);
172
+ let key = await chooseKey(options.key ? null : config.key, options.key, config);
149
173
  if (!key) process.exit(0);
150
174
 
175
+ // --force: çakışma denetimi yok, klasör keyin içeriğine yayınlanır
176
+ const base = !options.force && config.deckId && Number.isInteger(config.baseVersion)
177
+ ? { deckId: config.deckId, version: config.baseVersion }
178
+ : null;
151
179
  const title = readTitle(root, kind);
152
180
  const send = async () => {
153
181
  console.log(`\n[Pro Sicht] ${files.length} dosya (${formatBytes(total)}) yukleniyor...`);
154
182
  try {
155
- return await upload(host, key, files, title, kind);
183
+ return await upload(host, key, files, title, kind, base);
156
184
  } catch (error) {
157
185
  return fail(`${host} adresine ulasilamadi: ${error.cause?.code || error.message}`);
158
186
  }
@@ -161,28 +189,37 @@ async function publish(options = {}) {
161
189
  // kayıtlı key panelden yenilenmişse bir kez yenisini sor ve tekrar dene
162
190
  if (result.status === 401 && key === config.key && !options.key && !options.yes) {
163
191
  console.log('Kayitli key gecersiz; panelde yenilenmis olabilir.');
164
- key = await chooseKey(null, null);
192
+ key = await chooseKey(null, null, config);
165
193
  if (!key) process.exit(0);
166
194
  result = await send();
167
195
  }
168
196
  if (result.status !== 200 || !result.body.url) explain(result.status, result.body);
169
197
 
170
- const { url, privacy, version } = result.body;
198
+ const { url, privacy, version, deckId, role } = result.body;
171
199
  console.log(`Yayinlandi (${label}, surum ${version}): ${url}`);
172
- if (privacy === 'private') {
200
+ // ortak çalışan gizliliği değiştiremez; yalnızca sahibi
201
+ if (role === 'collaborator') {
202
+ console.log(privacy === 'private'
203
+ ? 'Gizlilik: yalnizca sahibi ve ortak calisanlar gorebilir. Paylasmayi sahibi panelden acar.'
204
+ : `Gizlilik: ${privacy === 'password' ? 'baglanti ve sifreyle' : 'baglantiya sahip herkes'} gorebilir (sahibinin ayari).`);
205
+ } else if (privacy === 'private') {
173
206
  console.log('Gizlilik: yalnizca siz gorebilirsiniz. Paylasmak icin panelden "Baglantiya sahip herkes" secin.');
174
207
  } else {
175
208
  console.log('Gizlilik: baglantiya sahip herkes gorebilir.');
176
209
  }
177
210
 
211
+ // baseVersion her yayında güncellenir: sonraki yayın bu sürümün üzerine kurulu sayılır
212
+ const published = { deckId, url, baseVersion: version };
178
213
  if (config.key !== key || config.host !== host) {
179
214
  const remember = options.yes ? true : await ask({
180
215
  type: 'confirm', name: 'save', initial: true,
181
216
  message: 'Key ve adres bu klasor icin kaydedilsin mi? (.prosicht/, git disi)'
182
217
  });
183
- if (remember) saveConfig(root, { host, key, deckId: result.body.deckId, url });
218
+ const rest = { ...config };
219
+ delete rest.key;
220
+ saveConfig(root, remember ? { ...rest, host, key, ...published } : { ...rest, host, ...published });
184
221
  } else {
185
- saveConfig(root, { ...config, deckId: result.body.deckId, url });
222
+ saveConfig(root, { ...config, ...published });
186
223
  }
187
224
  console.log('');
188
225
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prosicht",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Pro Sicht AI Standards CLI: AGENTS.md sablonlari, sunum ve blog altyapisi",
5
5
  "main": "index.js",
6
6
  "bin": {